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/build/codeblocks/nana.cbp b/build/codeblocks/nana.cbp index 83985a24..fb1a1b55 100644 --- a/build/codeblocks/nana.cbp +++ b/build/codeblocks/nana.cbp @@ -81,6 +81,7 @@ + diff --git a/build/codeblocks/nana.layout b/build/codeblocks/nana.layout index 805917bc..92b352dc 100644 --- a/build/codeblocks/nana.layout +++ b/build/codeblocks/nana.layout @@ -1,34 +1,9 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -36,86 +11,51 @@ - - - - - - - - - - + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -126,9 +66,19 @@ - + - + + + + + + + + + + + @@ -136,19 +86,69 @@ + + + + + + + + + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/vc2013/nana.vcxproj b/build/vc2013/nana.vcxproj index 7e216758..610054bc 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 @@ + @@ -213,6 +222,7 @@ + @@ -247,7 +257,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file + diff --git a/build/vc2013/nana.vcxproj.filters b/build/vc2013/nana.vcxproj.filters index f0f82981..6ade66c7 100644 --- a/build/vc2013/nana.vcxproj.filters +++ b/build/vc2013/nana.vcxproj.filters @@ -58,6 +58,51 @@ {e95b4a72-643f-4416-af95-b0bbaf7f0c57} + + {d421a05b-b868-4c3d-bdac-ff57d09f8d07} + + + {64c22f90-dce2-40dc-be98-edc9fe8951e8} + + + {91301ff5-79ac-40cc-a6db-bca2097ea763} + + + {81c62a28-ef5f-43f7-a6ce-e6c186cb411a} + + + {4a1db6f0-a4ee-4514-8e89-54ef9d52e3b8} + + + {c92e890a-ffd5-4efd-8b37-78ed9bbea1e1} + + + {63401f37-26a3-423e-87e6-6840344c3056} + + + {61385c08-f06b-4cf3-8e05-9b47d546164b} + + + {e9eadc97-4a14-4a4e-bb52-52d3a20e2693} + + + {5667eac1-0887-4936-9502-eac4dd8c1c1d} + + + {e6c4a4d9-b4b5-4c56-bb2a-486f3f777ecb} + + + {ac132633-f80c-4983-8e47-fa7fc17f666e} + + + {ee3e9e63-7221-409e-9659-25864e576e16} + + + {ef87057b-dff2-40aa-a05e-9dcd0b335c30} + + + {8ea232cf-bd7c-47e3-a694-6a8898b677d7} + @@ -74,6 +119,9 @@ Source Files\nana\detail\win32 + + + Source Files\nana\filesystem Source Files\nana\filesystem @@ -301,4 +349,374 @@ Source Files\nana\gui\widgets - \ No newline at end of file + + + Source Files\nana\gui\widgets + + + + + Header Files\gui\widgets + + + Header Files\gui\widgets + + + Header Files\gui\widgets + + + Header Files\gui\widgets + + + Header Files\gui\widgets + + + Header Files\gui\widgets + + + Header Files\gui\widgets + + + Header Files\gui\widgets + + + Header Files\gui\widgets + + + Header Files\gui\widgets + + + Header Files\gui\widgets + + + Header Files\gui\widgets + + + Header Files\gui\widgets + + + Header Files\gui\widgets + + + Header Files\gui\widgets + + + Header Files\gui\widgets + + + Header Files\gui\widgets + + + Header Files\gui\widgets + + + Header Files\gui\widgets + + + Header Files\gui\widgets + + + Header Files\gui\widgets + + + Header Files\gui\widgets + + + Header Files\gui\widgets + + + Header Files\gui\detail + + + Header Files\gui\detail + + + Header Files\gui\detail + + + Header Files\gui\detail + + + Header Files\gui\detail + + + Header Files\gui\detail + + + Header Files\gui\detail + + + Header Files\gui\detail + + + Header Files\gui\detail + + + Header Files\gui\detail + + + Header Files\gui\detail + + + Header Files\gui\detail + + + Header Files\gui\detail + + + Header Files\gui\detail + + + Header Files\gui\detail + + + Header Files\gui\detail + + + Header Files\gui\detail + + + Header Files\gui\detail + + + Header Files\gui\detail + + + Header Files\gui\detail + + + Header Files\gui\detail + + + Header Files\gui + + + Header Files\gui + + + Header Files\gui + + + Header Files\gui + + + Header Files\gui + + + Header Files\gui + + + Header Files\gui + + + Header Files\gui + + + Header Files\gui + + + Header Files\gui + + + Header Files\gui + + + Header Files\gui + + + Header Files\gui + + + Header Files\gui + + + Header Files\gui + + + Header Files\gui + + + Header Files\gui + + + Header Files\gui\widgets\detail + + + Header Files\gui\widgets\detail + + + Header Files\gui\widgets\skeletons + + + Header Files\gui\widgets\skeletons + + + Header Files\gui\widgets\skeletons + + + Header Files\gui\widgets\skeletons + + + Header Files\gui\widgets\skeletons + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files\detail\linux_X11 + + + Header Files\detail\linux_X11 + + + Header Files\detail\linux_X11 + + + Header Files\detail\win32 + + + Header Files\extrlib + + + Header Files\extrlib + + + Header Files\extrlib + + + Header Files\extrlib + + + Header Files\filesystem + + + Header Files\filesystem + + + Header Files\filesystem + + + Header Files\paint + + + Header Files\paint + + + Header Files\paint + + + Header Files\paint + + + Header Files\paint + + + Header Files\paint + + + Header Files\paint + + + Header Files\paint\detail + + + Header Files\paint\detail + + + Header Files\paint\detail + + + Header Files\paint\detail + + + Header Files\paint\detail + + + Header Files\paint\detail + + + Header Files\paint\detail + + + Header Files\pat + + + Header Files\system + + + Header Files\system + + + Header Files\system + + + Header Files\system + + + Header Files\threads + + + Header Files\gui\widgets + + + 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..d4f7f5d3 --- /dev/null +++ b/build/vc2015/nana.vcxproj @@ -0,0 +1,264 @@ + + + + + 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..81495341 --- /dev/null +++ b/build/vc2015/nana.vcxproj.filters @@ -0,0 +1,310 @@ + + + + + {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 + + + Source Files\nana\gui\widgets + + + \ No newline at end of file diff --git a/include/nana/audio/detail/audio_stream.hpp b/include/nana/audio/detail/audio_stream.hpp index 694089bd..5fe9d39c 100644 --- a/include/nana/audio/detail/audio_stream.hpp +++ b/include/nana/audio/detail/audio_stream.hpp @@ -12,19 +12,19 @@ namespace nana{ namespace audio{ #pragma pack(1) struct master_riff_chunk { - unsigned long ckID; //"RIFF" - unsigned long cksize; - unsigned long waveID; //"WAVE" + unsigned ckID; //"RIFF" + unsigned cksize; + unsigned waveID; //"WAVE" }; struct format_chunck { - unsigned long ckID; //"fmt " - unsigned long cksize; + unsigned ckID; //"fmt " + unsigned cksize; unsigned short wFormatTag; unsigned short nChannels; - unsigned long nSamplePerSec; - unsigned long nAvgBytesPerSec; + unsigned nSamplePerSec; + unsigned nAvgBytesPerSec; unsigned short nBlockAlign; unsigned short wBitsPerSample; }; @@ -32,19 +32,19 @@ namespace nana{ namespace audio{ #elif defined(NANA_LINUX) struct master_riff_chunk { - unsigned long ckID; //"RIFF" - unsigned long cksize; - unsigned long waveID; //"WAVE" + unsigned ckID; //"RIFF" + unsigned cksize; + unsigned waveID; //"WAVE" }__attribute__((packed)); struct format_chunck { - unsigned long ckID; //"fmt " - unsigned long cksize; + unsigned ckID; //"fmt " + unsigned cksize; unsigned short wFormatTag; unsigned short nChannels; - unsigned long nSamplePerSec; - unsigned long nAvgBytesPerSec; + unsigned nSamplePerSec; + unsigned nAvgBytesPerSec; unsigned short nBlockAlign; unsigned short wBitsPerSample; }__attribute__((packed)); @@ -55,8 +55,8 @@ namespace nana{ namespace audio{ { struct chunck { - unsigned long ckID; - unsigned long cksize; + unsigned ckID; + unsigned cksize; }; public: bool open(const nana::string& 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/basic_types.hpp b/include/nana/basic_types.hpp index a48d9ece..da37272d 100644 --- a/include/nana/basic_types.hpp +++ b/include/nana/basic_types.hpp @@ -306,7 +306,7 @@ namespace nana color(unsigned red, unsigned green, unsigned blue, double alpha); /// Initializes the color with a CSS-like rgb string. - color(std::string css_rgb); + explicit color(std::string css_rgb); color& alpha(double); ///< Sets alpha channel color& from_rgb(unsigned red, unsigned green, unsigned blue); ///< immutable alpha channel @@ -406,10 +406,10 @@ namespace nana struct rectangle { - rectangle(); ///< a zero-size rectangle at (0, 0). + rectangle(); ///< a zero-size rectangle at (0, 0). rectangle(int x, int y, unsigned width, unsigned height); - rectangle(const size &); ///< a rectangle with specified size at coordinate (0, 0). - rectangle(const point&, const size& = size()); + explicit rectangle(const size &); ///< a rectangle with specified size at coordinate (0, 0). + explicit rectangle(const point&, const size& = size()); bool operator==(const rectangle& rhs) const; bool operator!=(const rectangle& rhs) const; 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..e8db5fff 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, @@ -165,6 +172,12 @@ namespace detail HMODULE ole32_; }; + struct window_icons + { + ::nana::paint::image sml_icon; + ::nana::paint::image big_icon; + }; + platform_spec(); const font_ptr_t& default_native_font() const; @@ -175,11 +188,11 @@ namespace detail static platform_spec& instance(); - void keep_window_icon(native_window_type, const nana::paint::image&); + void keep_window_icon(native_window_type, const paint::image&sml_icon, const paint::image& big_icon); void release_window_icon(native_window_type); private: font_ptr_t def_font_ptr_; - std::map iconbase_; + std::map iconbase_; }; }//end namespace detail diff --git a/include/nana/filesystem/filesystem.hpp b/include/nana/filesystem/filesystem.hpp new file mode 100644 index 00000000..6647747d --- /dev/null +++ b/include/nana/filesystem/filesystem.hpp @@ -0,0 +1,468 @@ +/* + * A ISO C++ filesystem Implementation + * Nana C++ Library(http://www.nanapro.org) + * Copyright(C) 2003-2015 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.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 + +#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 + { + using path_type = filesystem::path; + path_type m_path; + + attribute attr{}; + //file_status m_status; + + directory_entry(){} + directory_entry(const path_type& p, bool is_directory, uintmax_t size) + :m_path{p}, attr{size, is_directory} + {} + + void assign (const path_type& p){ m_path=p;} + void replace_filename(const path_type& p){ m_path=p;} + + //file_status status() const; + + operator const path_type&() const {return m_path;}; + const path_type& 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(path(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; + bool is_directory = false; + unsigned size = 0; + if(stat((path_ + dnt->d_name).c_str(), &fst) == 0) + { + is_directory = (0 != S_ISDIR(fst.st_mode)); + size = fst.st_size; + } + value_ = value_type(static_cast(nana::charset(dnt->d_name)), is_directory, size); + 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(path(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; + } + } + + nana::string d_name = nana::charset(dnt->d_name); + struct stat fst; + if(stat((path_ + "/" + dnt->d_name).c_str(), &fst) == 0) + value_ = value_type(std::move(d_name), (0 != S_ISDIR(fst.st_mode)), fst.st_size); + else + value_.m_path = path(std::move(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 659f4316..1ce944a9 100644 --- a/include/nana/gui/detail/basic_window.hpp +++ b/include/nana/gui/detail/basic_window.hpp @@ -15,17 +15,21 @@ #include "drawer.hpp" #include "events_holder.hpp" #include "widget_colors.hpp" +#include "widget_notifier_interface.hpp" #include #include #include -#include namespace nana{ - class widget; //declaration ofr nana/widgets/widget.hpp namespace detail { struct basic_window; + enum class visible_state + { + invisible, visible, displayed + }; + class caret_descriptor { public: @@ -42,17 +46,13 @@ namespace detail bool visible() const; ::nana::size size() const; void size(const ::nana::size&); - void update(); - private: - void _m_visible(bool isshow); 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 out_of_range_; ::nana::rectangle effective_range_; };//end class caret_descriptor @@ -76,11 +76,6 @@ namespace detail { using container = std::vector; - struct root_context - { - bool focus_changed; - }; - enum class update_state { none, lazy, refresh @@ -94,11 +89,11 @@ namespace detail //basic_window //@brief: constructor for the root window - basic_window(basic_window* owner, widget*, category::root_tag**); + basic_window(basic_window* owner, std::unique_ptr&&, category::root_tag**); template - basic_window(basic_window* parent, const rectangle& r, widget* wdg, Category**) - : widget_ptr(wdg), other(Category::value) + basic_window(basic_window* parent, std::unique_ptr&& wdg_notifier, const rectangle& r, Category**) + : widget_notifier(std::move(wdg_notifier)), other(Category::value) { drawer.bind(this); if(parent) @@ -118,9 +113,13 @@ 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. + + basic_window * seek_non_lite_widget_ancestor() const; public: //Override event_holder bool set_events(const std::shared_ptr&) override; @@ -151,7 +150,7 @@ namespace detail basic_window* root_widget; //A pointer refers to the root basic window, if the window is a root, the pointer refers to itself. paint::graphics* root_graph; //Refer to the root buffer graphics cursor predef_cursor; - widget* const widget_ptr; + std::unique_ptr widget_notifier; struct flags_type { @@ -165,9 +164,10 @@ namespace detail bool dropable :1; //Whether the window has make mouse_drop event. bool fullscreen :1; //When the window is maximizing whether it fit for fullscreen. bool borderless :1; - bool make_bground_declared : 1; //explicitly make bground for bground effects - bool ignore_menubar_focus : 1; //A flag indicates whether the menubar sets the focus. - unsigned Reserved :20; + bool make_bground_declared : 1; //explicitly make bground for bground effects + bool ignore_menubar_focus : 1; //A flag indicates whether the menubar sets the focus. + bool ignore_mouse_focus : 1; //A flag indicates whether the widget accepts focus when clicking on it + unsigned Reserved :19; unsigned char tab; //indicate a window that can receive the keyboard TAB mouse_action action; }flags; @@ -202,7 +202,6 @@ namespace detail std::vector effects_edge_nimbus; basic_window* focus{nullptr}; basic_window* menubar{nullptr}; - root_context context; bool ime_enabled{false}; #if defined(NANA_WINDOWS) cursor running_cursor{ nana::cursor::arrow }; @@ -210,7 +209,7 @@ namespace detail cursor state_cursor{nana::cursor::arrow}; basic_window* state_cursor_window{ nullptr }; - std::function draw_through; ///< A draw through renderer for root widgets. + std::function draw_through; // A draw through renderer for root widgets. }; const category::flags category; diff --git a/include/nana/gui/detail/bedrock.hpp b/include/nana/gui/detail/bedrock.hpp index 1ae2a41d..91bc71ed 100644 --- a/include/nana/gui/detail/bedrock.hpp +++ b/include/nana/gui/detail/bedrock.hpp @@ -40,9 +40,11 @@ namespace detail struct thread_context; + class flag_guard; + ~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..dfd98e88 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 @@ -40,7 +46,7 @@ namespace nana virtual void resizing(graph_reference, const arg_resizing&); virtual void resized(graph_reference, const arg_resized&); virtual void move(graph_reference, const arg_move&); - virtual void click(graph_reference, const arg_mouse&); + virtual void click(graph_reference, const arg_click&); virtual void dbl_click(graph_reference, const arg_mouse&); virtual void mouse_enter(graph_reference, const arg_mouse&); virtual void mouse_move(graph_reference, const arg_mouse&); @@ -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 }; @@ -93,7 +100,7 @@ namespace nana void bind(basic_window*); void typeface_changed(); - void click(const arg_mouse&); + void click(const arg_click&); void dbl_click(const arg_mouse&); void mouse_enter(const arg_mouse&); void mouse_move(const arg_mouse&); @@ -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 91daf849..ea2b810e 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.begin(); i != nimbus.end(); ++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) @@ -98,13 +137,13 @@ namespace nana{ void _m_render_edge_nimbus(core_window_t* wd, const nana::rectangle & visual) { - nana::rectangle r(visual); + auto r = visual; r.pare_off(-static_cast(weight())); - nana::rectangle good_r; - if(overlap(r, wd->root_graph->size(), good_r)) + 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); @@ -131,7 +170,7 @@ namespace nana{ pixbuf.pixel(0, r.height - 1, px2); pixbuf.pixel(r.width - 1, r.height - 1, px3); - pixbuf.paste(wd->root, r.x, r.y); + pixbuf.paste(wd->root, { r.x, r.y }); std::vector overlaps; if(window_layer::read_overlaps(wd, visual, overlaps)) diff --git a/include/nana/gui/detail/general_events.hpp b/include/nana/gui/detail/general_events.hpp index 1a4bf972..cdb6ac3a 100644 --- a/include/nana/gui/detail/general_events.hpp +++ b/include/nana/gui/detail/general_events.hpp @@ -46,7 +46,7 @@ namespace nana class event_arg { public: - virtual ~event_arg(); + virtual ~event_arg() = default; /// ignorable handlers behind the current one in a chain of event handlers will not get called. void stop_propagation() const; @@ -425,11 +425,19 @@ namespace nana event_code evt_code; ///< ::nana::window window_handle; ///< A handle to the event window ::nana::point pos; ///< cursor position in the event window + ::nana::mouse button; ///< indicates a button which triggers the event + bool left_button; ///< mouse left button is pressed? bool mid_button; ///< mouse middle button is pressed? bool right_button; ///< mouse right button is pressed? bool shift; ///< keyboard Shift is pressed? bool ctrl; ///< keyboard Ctrl is pressed? + + /// Checks if left button is operated, + bool is_left_button() const + { + return (event_code::mouse_move == evt_code ? left_button : (mouse::left_button == button)); + } }; /// in arg_wheel event_code is event_code::mouse_wheel @@ -510,31 +518,37 @@ namespace nana ::nana::window window_handle; ///< A handle to the event window }; + struct arg_click : public event_arg + { + ::nana::window window_handle; ///< A handle to the event window + bool by_mouse; ///< Determines whether the event is emitted by clicking mouse button + }; + /// provides some fundamental events that every widget owns. struct general_events { virtual ~general_events(){} - basic_event mouse_enter; ///< the cursor enters the window - basic_event mouse_move; ///< the cursor moves on the window - basic_event mouse_leave; ///< the cursor leaves the window - basic_event mouse_down; ///< the user presses the mouse button - basic_event mouse_up; ///< the user presses the mouse button - basic_event click; ///< the window is clicked, but occurs after mouse_down and before mouse_up - basic_event dbl_click; ///< the window is double clicked - basic_event mouse_wheel; ///< the mouse wheel rotates while the window has focus + basic_event mouse_enter; ///< the cursor enters the window + basic_event mouse_move; ///< the cursor moves on the window + basic_event mouse_leave; ///< the cursor leaves the window + basic_event mouse_down; ///< the user presses the mouse button + basic_event mouse_up; ///< the user presses the mouse button + basic_event click; ///< the window is clicked, but occurs after mouse_down and before mouse_up + basic_event dbl_click; ///< the window is double clicked + basic_event mouse_wheel; ///< the mouse wheel rotates while the window has focus basic_event mouse_dropfiles; ///< the mouse drops some external data while the window enable accepting files - basic_event expose; ///< the visibility changes - basic_event focus; ///< the window receives or loses keyboard focus + basic_event expose; ///< the visibility changes + basic_event focus; ///< the window receives or loses keyboard focus basic_event key_press; ///< a key is pressed while the window has focus. event code is event_code::key_press basic_event key_release; ///< a key is released while the window has focus. event code is event_code::key_release - basic_event key_char; ///< a character, whitespace or backspace is pressed. event code is event_code::key_char - basic_event shortkey; ///< a defined short key is pressed. event code is event_code::shortkey + basic_event key_char; ///< a character, whitespace or backspace is pressed. event code is event_code::key_char + basic_event shortkey; ///< a defined short key is pressed. event code is event_code::shortkey - basic_event move; ///< the window changes position - basic_event resizing; ///< the window is changing its size - basic_event resized; ///< the window is changing its size + basic_event move; ///< the window changes position + basic_event resizing; ///< the window is changing its size + basic_event resized; ///< the window is changing its size - basic_event destroy; ///< the window is destroyed, but occurs when all children have been destroyed + basic_event destroy; ///< the window is destroyed, but occurs when all children have been destroyed }; namespace detail diff --git a/include/nana/gui/detail/inner_fwd.hpp b/include/nana/gui/detail/inner_fwd.hpp index 00a1f24e..ffe18a52 100644 --- a/include/nana/gui/detail/inner_fwd.hpp +++ b/include/nana/gui/detail/inner_fwd.hpp @@ -1,7 +1,7 @@ /* * Inner Forward Declaration * Nana C++ Library(http://www.nanapro.org) -* Copyright(C) 2003-2014 Jinhao(cnjinhao@hotmail.com) +* Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -19,37 +19,6 @@ namespace nana{ namespace detail { - struct signals - { - enum class code - { - caption, - read_caption, - destroy, - size, - end - }; - - union - { - const nana::char_t* caption; - nana::string * str; - struct - { - unsigned width; - unsigned height; - }size; - }info; - }; - - class signal_invoker_interface - { - public: - virtual ~signal_invoker_interface() - {} - - virtual void call_signal(signals::code, const signals&) = 0; - }; } } #endif //NANA_GUI_INNER_FWD_HPP diff --git a/include/nana/gui/detail/inner_fwd_implement.hpp b/include/nana/gui/detail/inner_fwd_implement.hpp index 5384aec4..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) @@ -170,34 +169,6 @@ namespace nana{ std::map table_; }; - - - class signal_manager - { - typedef basic_window core_window_t; - public: - void make(core_window_t* wd, signal_invoker_interface* si) - { - if (si) - table_[wd].reset(si); - else - table_.erase(wd); - } - - void umake(core_window_t * wd) - { - table_.erase(wd); - } - - void call_signal(core_window_t * wd, signals::code code, const signals& s) - { - auto i = table_.find(wd); - if (i != table_.end()) - i->second->call_signal(code, s); - } - private: - std::map> table_; - }; } }//end namespace nana #endif //NANA_GUI_INNER_FWD_IMPLEMENT_HPP diff --git a/include/nana/gui/detail/native_window_interface.hpp b/include/nana/gui/detail/native_window_interface.hpp index 39cd7c49..5db4a645 100644 --- a/include/nana/gui/detail/native_window_interface.hpp +++ b/include/nana/gui/detail/native_window_interface.hpp @@ -44,7 +44,8 @@ namespace detail #endif static void enable_dropfiles(native_window_type, bool); static void enable_window(native_window_type, bool); - static bool window_icon(native_window_type, const paint::image&); + // (On Windows) The system displays the large icon in the ALT+TAB dialog box, and the small icon in the window caption. + static bool window_icon(native_window_type, const paint::image& big_icon, const paint::image& small_icon); static void activate_owner(native_window_type); static void activate_window(native_window_type); static void close_window(native_window_type); diff --git a/include/nana/gui/detail/widget_notifier_interface.hpp b/include/nana/gui/detail/widget_notifier_interface.hpp new file mode 100644 index 00000000..204bdeea --- /dev/null +++ b/include/nana/gui/detail/widget_notifier_interface.hpp @@ -0,0 +1,39 @@ +/* + * Widget Notifier Interface + * Nana C++ Library(http://www.nanapro.org) + * Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com) + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * @file: nana/gui/detail/widget_notifier_interface.hpp + */ + +#ifndef NANA_GUI_DETAIL_WIDGET_NOTIFIER_INTERFACE_HEADER +#define NANA_GUI_DETAIL_WIDGET_NOTIFIER_INTERFACE_HEADER +#include +#include + +namespace nana +{ + class widget; //forward declaration + + namespace detail + { + class widget_notifier_interface + { + public: + virtual ~widget_notifier_interface() = default; + + static std::unique_ptr get_notifier(widget*); //defined in nana/gui/widgets/widget.cpp + + virtual widget* widget_ptr() const = 0; + virtual void destroy() = 0; + virtual std::wstring caption() = 0; + virtual void caption(std::wstring) = 0; + }; + } +} + +#endif \ No newline at end of file diff --git a/include/nana/gui/detail/window_manager.hpp b/include/nana/gui/detail/window_manager.hpp index 37c9c150..ec91fe1f 100644 --- a/include/nana/gui/detail/window_manager.hpp +++ b/include/nana/gui/detail/window_manager.hpp @@ -42,24 +42,7 @@ namespace nana namespace nana{ namespace detail { - template - class signal_invoker_mf - : public signal_invoker_interface - { - public: - signal_invoker_mf(T& obj, void(T::*mf)(signals::code, const signals&)) - : obj_(obj), - mf_(mf) - {} - - void call_signal(signals::code code, const signals& s) override - { - (obj_.*mf_)(code, s); - } - private: - T& obj_; - void(T::*mf_)(signals::code, const signals&); - }; + class widget_notifier_interface; //forward declaration struct root_misc; @@ -87,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(); @@ -103,16 +84,7 @@ namespace detail mutex_type & internal_lock() const; void all_handles(std::vector&) const; - template - void attach_signal(core_window_t* wd, T& obj, void(Concept::*mf)(signals::code, const signals&)) - { - return _m_attach_signal(wd, new signal_invoker_mf(obj, mf)); - } - - void signal_fire_caption(core_window_t*, const nana::char_t*); - nana::string signal_fire_caption(core_window_t*); void event_filter(core_window_t*, bool is_make, event_code); - void default_icon(const nana::paint::image&); bool available(core_window_t*); bool available(core_window_t *, core_window_t*); @@ -121,6 +93,7 @@ namespace detail core_window_t* create_root(core_window_t*, bool nested, rectangle, const appearance&, widget*); core_window_t* create_widget(core_window_t*, const rectangle&, bool is_lite, widget*); core_window_t* create_frame(core_window_t*, const rectangle&, widget*); + bool insert_frame(core_window_t* frame, native_window); bool insert_frame(core_window_t* frame, core_window_t*); void close(core_window_t*); @@ -133,7 +106,8 @@ namespace detail //@brief: Delete window handle, the handle type must be a root and a frame. void destroy_handle(core_window_t*); - void icon(core_window_t*, const paint::image&); + void default_icon(const paint::image& small_icon, const paint::image& big_icon); + void icon(core_window_t*, const paint::image& small_icon, const paint::image& big_icon); //show //@brief: show or hide a window @@ -150,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); @@ -188,7 +162,6 @@ namespace detail core_window_t* find_shortkey(native_window_type, unsigned long key); private: - void _m_attach_signal(core_window_t*, signal_invoker_interface*); void _m_disengage(core_window_t*, core_window_t* for_new); void _m_destroy(core_window_t*); void _m_move_core(core_window_t*, const point& delta); @@ -200,8 +173,6 @@ namespace detail struct wdm_private_impl; wdm_private_impl * const impl_; - signals signals_; - struct attribute { struct captured diff --git a/include/nana/gui/layout_utility.hpp b/include/nana/gui/layout_utility.hpp index fae4f4c9..51d4d4ea 100644 --- a/include/nana/gui/layout_utility.hpp +++ b/include/nana/gui/layout_utility.hpp @@ -1,14 +1,13 @@ /* * Utility Implementation - * Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com) + * Nana C++ Library(http://www.nanapro.org) + * Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) * * @file: nana/gui/layout_utility.hpp - * - * */ #ifndef NANA_GUI_LAYOUT_UTILITY_HPP diff --git a/include/nana/gui/programming_interface.hpp b/include/nana/gui/programming_interface.hpp index c2430ccd..167fd8d8 100644 --- a/include/nana/gui/programming_interface.hpp +++ b/include/nana/gui/programming_interface.hpp @@ -48,13 +48,6 @@ namespace API //@brief: The interfaces defined in namespace dev are used for developing the nana.gui namespace dev { - template - void attach_signal(window wd, Object& object, void (Concept::*f)(::nana::detail::signals::code, const ::nana::detail::signals&)) - { - using namespace ::nana::detail; - bedrock::instance().wd_manager.attach_signal(reinterpret_cast(wd), object, f); - } - bool set_events(window, const std::shared_ptr&); template @@ -68,7 +61,7 @@ namespace API widget_colors* get_scheme(window); void attach_drawer(widget&, drawer_trigger&); - nana::string window_caption(window); + nana::string window_caption(window) throw(); void window_caption(window, nana::string); window create_window(window, bool nested, const rectangle&, const appearance&, widget* attached); @@ -119,10 +112,12 @@ namespace API } } - void window_icon_default(const paint::image&); - void window_icon(window, const paint::image&); + void window_icon_default(const paint::image& small_icon, const paint::image& big_icon = {}); + void window_icon(window, const paint::image& small_icon, const paint::image& big_icon = {}); + bool empty_window(window); ///< Determines whether a window is existing. bool is_window(window); ///< Determines whether a window is existing, equal to !empty_window. + bool is_destroying(window); ///< Determines whether a window is destroying void enable_dropfiles(window, bool); /// \brief Retrieves the native window of a Nana.GUI window. @@ -300,6 +295,9 @@ namespace API nana::mouse_action mouse_action(window); nana::element_state element_state(window); + + bool ignore_mouse_focus(window, bool ignore); ///< Enables/disables the mouse focus, it returns the previous state + bool ignore_mouse_focus(window); ///< Determines whether the mouse focus is enabled }//end namespace API }//end namespace nana 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/button.hpp b/include/nana/gui/widgets/button.hpp index e57f3b12..3c0eaf7d 100644 --- a/include/nana/gui/widgets/button.hpp +++ b/include/nana/gui/widgets/button.hpp @@ -15,7 +15,6 @@ #include "widget.hpp" #include - namespace nana{ namespace drawerbase { @@ -46,12 +45,13 @@ namespace nana{ void mouse_up(graph_reference, const arg_mouse&) override; void key_char(graph_reference, const arg_keyboard&) override; void key_press(graph_reference, const arg_keyboard&) override; + void key_release(graph_reference, const arg_keyboard&) override; void focus(graph_reference, const arg_focus&) override; private: - void _m_draw(graph_reference); void _m_draw_title(graph_reference, bool enabled); void _m_draw_background(graph_reference); void _m_draw_border(graph_reference); + void _m_press(graph_reference, bool); private: widget* wdg_{nullptr}; paint::graphics* graph_{nullptr}; @@ -101,8 +101,6 @@ namespace nana{ bool transparent() const; button& edge_effects(bool enable); - private: - void _m_shortkey(); private: //Overrides widget virtual functions void _m_complete_creation() override; diff --git a/include/nana/gui/widgets/checkbox.hpp b/include/nana/gui/widgets/checkbox.hpp index 22f87fff..c0ebee4f 100644 --- a/include/nana/gui/widgets/checkbox.hpp +++ b/include/nana/gui/widgets/checkbox.hpp @@ -37,7 +37,6 @@ namespace drawerbase public: implement * impl() const; private: - void _m_draw(graph_reference); void _m_draw_background(graph_reference); void _m_draw_checkbox(graph_reference, unsigned first_line_height); void _m_draw_title(graph_reference); @@ -89,7 +88,7 @@ namespace drawerbase std::size_t checked() const; ///< Retrieves the index of the checkbox which is checked. std::size_t size() const; private: - void _m_checked(const arg_mouse&); + void _m_checked(const arg_click&); void _m_destroy(const arg_destroy&); private: std::vector ui_container_; diff --git a/include/nana/gui/widgets/combox.hpp b/include/nana/gui/widgets/combox.hpp index 16b22faf..beb1cece 100644 --- a/include/nana/gui/widgets/combox.hpp +++ b/include/nana/gui/widgets/combox.hpp @@ -3,8 +3,8 @@ * Nana C++ Library(http://www.nanapro.org) * Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com) * - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) * * @file: nana/gui/widgets/combox.hpp @@ -43,7 +43,7 @@ namespace nana }; class drawer_impl; - + class trigger : public drawer_trigger { @@ -101,7 +101,7 @@ namespace nana throw std::runtime_error("combox::item_proxy.value() invalid type of value"); return *p; } - + template item_proxy& value(const T& t) { @@ -169,7 +169,7 @@ namespace nana combox(window, nana::string, bool visible = true); combox(window, const nana::char_t*, bool visible = true); combox(window, const rectangle& r = rectangle(), bool visible = true); - + void clear(); void editable(bool); bool editable() const; @@ -209,8 +209,8 @@ namespace nana _m_erase(p.get()); } - /// \brief Set user-defined item renderer object. - /// It is an address therefore the user should not destroy the renderer object + /// \brief Set user-defined item renderer object. + /// It is an address therefore the user should not destroy the renderer object /// after it is set to the combox. Passing null_ptr cancels the user-defined renderer object. void renderer(item_renderer*); @@ -224,7 +224,7 @@ namespace nana const drawerbase::combox::drawer_impl& _m_impl() const; private: //Overrides widget's virtual functions - nana::string _m_caption() const override; + nana::string _m_caption() const throw() override; void _m_caption(nana::string&&) override; nana::any * _m_anyobj(std::size_t pos, bool alloc_if_empty) const override; }; diff --git a/include/nana/gui/widgets/date_chooser.hpp b/include/nana/gui/widgets/date_chooser.hpp index 9fc0ca8d..9a168bfb 100644 --- a/include/nana/gui/widgets/date_chooser.hpp +++ b/include/nana/gui/widgets/date_chooser.hpp @@ -45,7 +45,6 @@ namespace nana void week_name(unsigned index, const nana::string&); private: where _m_pos_where(graph_reference, const ::nana::point& pos); - void _m_draw(graph_reference); void _m_draw_topbar(graph_reference); void _m_make_drawing_basis(drawing_basis&, graph_reference, const nana::point& refpos); void _m_draw_pos(drawing_basis &, graph_reference, int x, int y, const nana::string&, bool primary, bool sel); diff --git a/include/nana/gui/widgets/detail/inline_widget.hpp b/include/nana/gui/widgets/detail/inline_widget.hpp new file mode 100644 index 00000000..6b40c7e3 --- /dev/null +++ b/include/nana/gui/widgets/detail/inline_widget.hpp @@ -0,0 +1,85 @@ +/** + * A Inline Widget Interface Definition + * Nana C++ Library(http://www.nanapro.org) + * Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com) + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * @file: nana/gui/widgets/detail/inline_widget.hpp + * + */ + +#ifndef NANA_GUI_INLINE_WIDGETS_HPP +#define NANA_GUI_INLINE_WIDGETS_HPP +#include "../../basis.hpp" + +namespace nana +{ + namespace detail + { + template + class inline_widget_indicator + { + public: + /// A type to index a item + using index_type = Index; + + /// A type to the value of the item + using value_type = Value; + + /// The destructor + virtual ~inline_widget_indicator() = default; + + /// Returns the host widget of the indicator + virtual ::nana::widget& host() const = 0; + + /// Modifies the value of a item specified by pos + virtual void modify(index_type pos, const value_type&) const = 0; + + /// Sends a signal that a specified item is selected + virtual void selected(index_type) = 0; + + /// Sends a signal that a specified item is hovered + virtual void hovered(index_type) = 0; + }; + + template + class inline_widget_notifier_interface + { + public: + /// A type to index a item + using index_type = Index; + + /// A type to the value of the item + using value_type = Value; + + /// A typedef name of a inline widget indicator + using inline_indicator = inline_widget_indicator; + + /// A type to the notifier interface that will be refered by the abstract factory pattern + using factory_interface = inline_widget_notifier_interface; + + /// The destructor + virtual ~inline_widget_notifier_interface() = default; + + /// A message to create the inline widget + virtual void create(window) = 0; + + /// A message to activate the inline widget to attach a specified item + virtual void activate(inline_indicator&, index_type) = 0; + + /// A message to resize the inline widget + virtual void resize(const size&) = 0; + + /// A message to set the value from the item + virtual void set(const value_type&) = 0; + + /// Determines whether to draw the background of the widget + virtual bool whether_to_draw() const = 0; + }; //end class inline_widget_notifier_interface + } +} + +#endif \ No newline at end of file diff --git a/include/nana/gui/widgets/detail/inline_widget_manager.hpp b/include/nana/gui/widgets/detail/inline_widget_manager.hpp new file mode 100644 index 00000000..ef5ec65b --- /dev/null +++ b/include/nana/gui/widgets/detail/inline_widget_manager.hpp @@ -0,0 +1,89 @@ +/** + * A Inline Widget Manager Implementation + * Nana C++ Library(http://www.nanapro.org) + * Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com) + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * @file: nana/gui/widgets/detail/inline_widget_manager.hpp + * + */ + +#ifndef NANA_GUI_INLINE_WIDGET_MANAGER_HPP +#define NANA_GUI_INLINE_WIDGET_MANAGER_HPP +#include "inline_widget.hpp" +#include +#include "../panel.hpp" + +#include + +namespace nana +{ + namespace detail + { + + template + class inline_widget_manager + { + using index_type = Index; + using value_type = Value; + using indicator_type = inline_widget_indicator; + using inline_widget = inline_widget_interface; + using factory = pat::abstract_factory; + + struct widget_holder + { + panel docker; + std::unique_ptr widget_ptr; + }; + public: + void set_window(window wd) + { + window_handle_ = wd; + } + + void set_factory(std::unique_ptr fac) + { + factory_.swap(fac); + } + + void place(point pos, const size& dimension, const size & visible_size, const indicator_type& indicator, index_type index) + { + auto holder = _m_append(); + holder->docker.move({ pos, visible_size }); + holder->widget_ptr->move({ point(), dimension }); + + holder->widget_ptr->activate(indicator, index); + } + private: + widget_holder* _m_append() + { + if (swap_widgets_.empty()) + { + widgets_.emplace_back(); + widgets_.back().swap(swap_widgets_.back()); + swap_widgets_.pop_back(); + } + else + { + + widgets_.emplace_back(new widget_holder); + auto & holder = widgets_.back(); + holder->docker.create(window_handle_, false); + holder->widget_ptr.swap(factory_->create()); + holder->widget_ptr->create(holder->docker->handle()); + } + return widgets_.back().get(); + } + private: + window window_handle_{nullptr}; + std::unique_ptr factory_; + std::vector> widgets_; + std::vector> swap_widgets_; + }; + } +} + +#endif diff --git a/include/nana/gui/widgets/group.hpp b/include/nana/gui/widgets/group.hpp new file mode 100644 index 00000000..82f34a5b --- /dev/null +++ b/include/nana/gui/widgets/group.hpp @@ -0,0 +1,87 @@ +/** + * A group widget implementation + * Nana C++ Library(http://www.nanaro.org) + * Copyright(C) 2015 Jinhao(cnjinhao@hotmail.com) + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * @file: nana/gui/widgets/group.hpp + * + * @Author: Stefan Pfeifer(st-321), Ariel Vina-Rodriguez (qPCR4vir) + * + * @brief group is a widget used to visually group and layout other widgets. + */ + +#ifndef NANA_GUI_WIDGETS_GROUP_HPP +#define NANA_GUI_WIDGETS_GROUP_HPP + +#include +#include + +namespace nana{ + class group + : public panel + { + struct implement; + public: + using field_reference = place::field_reference; + + /// The default construction + group(); + + /// The construction that creates the widget + group(window parent, const rectangle& = {}, bool visible = true); + + /// The construction that creates the widget and set the titel or caption + + group(window parent, ///< a handle to the parent + ::nana::string titel, ///< caption of the group + bool formatted = false, ///< Enable/disable the formatted text for the title + unsigned gap = 2, ///< betwen the content and the external limit + const rectangle& r = {} , + bool visible = true + ); + + + /// The destruction + ~group(); + + /// Adds an option for user selection + group& add_option(::nana::string); + + /// Enables/disables the radio mode which is single selection + group& radio_mode(bool); + + /// Returns the index of option in radio_mode, it throws a logic_error if radio_mode is false. + std::size_t option() const; + + /// Determines whether a specified option is checked, it throws an out_of_range if !(pos < number of options) + bool option_checked(std::size_t pos) const; + + group& enable_format_caption(bool format); + + group& collocate() throw(); + group& div(const char* div_str) throw(); + field_reference operator[](const char* field); + + template + Widget* create_child(const char* field, Args && ... args) + { + auto wdg = new Widget(handle(), std::forward(args)...); + _m_add_child(field, wdg); + return wdg; + } + private: + void _m_add_child(const char* field, widget*); + void _m_init(); + void _m_complete_creation() override; + ::nana::string _m_caption() const throw() override; + void _m_caption(::nana::string&&) override; + private: + std::unique_ptr impl_; + }; + +}//end namespace nana +#endif diff --git a/include/nana/gui/widgets/label.hpp b/include/nana/gui/widgets/label.hpp index ba6a5cf0..6d72cd4e 100644 --- a/include/nana/gui/widgets/label.hpp +++ b/include/nana/gui/widgets/label.hpp @@ -40,7 +40,7 @@ namespace nana void refresh(graph_reference) override; void mouse_move(graph_reference, const arg_mouse&) override; void mouse_leave(graph_reference, const arg_mouse&) override; - void click(graph_reference, const arg_mouse&) override; + void click(graph_reference, const arg_click&) override; private: impl_t * impl_; }; @@ -58,14 +58,15 @@ namespace nana label(); label(window, bool visible); label(window, const nana::string& text, bool visible = true); - label(window, const nana::char_t* text, bool visible = true); label(window, const rectangle& = {}, bool visible = true); label& transparent(bool); ///< Switchs the label widget to the transparent background mode. - bool transparent() const; + bool transparent() const throw(); label& format(bool); ///< Switches the format mode of the widget. label& add_format_listener(std::function); - /// \briefReturn the size of the text. If *allowed_width_in_pixel* is not zero, returns a + label& click_for(window associated_window) throw(); // as same as the "for" attribute of a label + + /// Returns the size of the text. If *allowed_width_in_pixel* is not zero, returns a /// "corrected" size that changes lines to fit the text into the specified width nana::size measure(unsigned allowed_width_in_pixel) const; diff --git a/include/nana/gui/widgets/listbox.hpp b/include/nana/gui/widgets/listbox.hpp index 8a2f1938..3661b8a3 100644 --- a/include/nana/gui/widgets/listbox.hpp +++ b/include/nana/gui/widgets/listbox.hpp @@ -15,9 +15,10 @@ #ifndef NANA_GUI_WIDGETS_LISTBOX_HPP #define NANA_GUI_WIDGETS_LISTBOX_HPP #include "widget.hpp" +#include "detail/inline_widget.hpp" +#include #include #include -//#include #include #include @@ -29,7 +30,58 @@ namespace nana { namespace listbox { - using size_type = std::size_t ; + using size_type = std::size_t; + + /// usefull for both absolute and display (sorted) positions + struct index_pair + { + size_type cat; //The pos of category + size_type item; //the pos of item in a category. + + index_pair(size_type cat_pos = 0, size_type item_pos = 0) + : cat(cat_pos), + item(item_pos) + {} + + bool empty() const + { + return (npos == cat); + } + + void set_both(size_type n) + { + cat = item = n; + } + + bool is_category() const + { + return (npos != cat && npos == item); + } + + bool is_item() const + { + return (npos != cat && npos != item); + } + + bool operator==(const index_pair& r) const + { + return (r.cat == cat && r.item == item); + } + + bool operator!=(const index_pair& r) const + { + return !this->operator==(r); + } + + bool operator>(const index_pair& r) const + { + return (cat > r.cat) || (cat == r.cat && item > r.item); + } + }; + + using selection = std::vector; + + using inline_notifier_interface = detail::inline_widget_notifier_interface; struct cell { @@ -114,58 +166,13 @@ namespace nana std::size_t pos_{0}; }; - /// usefull for both absolute and display (sorted) positions - struct index_pair - { - size_type cat; //The pos of category - size_type item; //the pos of item in a category. - index_pair(size_type cat_pos = 0, size_type item_pos = 0) - : cat(cat_pos), - item(item_pos) - {} - - bool empty() const - { - return (npos == cat); - } - - void set_both(size_type n) - { - cat = item = n; - } - - bool is_category() const - { - return (npos != cat && npos == item); - } - - bool is_item() const - { - return (npos != cat && npos != item); - } - - bool operator==(const index_pair& r) const - { - return (r.cat == cat && r.item == item); - } - - bool operator!=(const index_pair& r) const - { - return !this->operator==(r); - } - - bool operator>(const index_pair& r) const - { - return (cat > r.cat) || (cat == r.cat && item > r.item); - } - }; 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 + ///@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; @@ -344,6 +351,8 @@ namespace nana : public std::iterator < std::input_iterator_tag, cat_proxy > { public: + using inline_notifier_interface = drawerbase::listbox::inline_notifier_interface; + cat_proxy() = default; cat_proxy(essence_t*, size_type pos); cat_proxy(essence_t*, category_t*); @@ -366,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); @@ -423,6 +436,8 @@ namespace nana /// Behavior of Iterator bool operator!=(const cat_proxy&) const; + + void inline_factory(size_type column, pat::cloneable> factory); private: void _m_append(std::vector && cells); void _m_cat_by_pos(); @@ -473,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, @@ -499,19 +552,23 @@ By \a clicking on a header the list get \a reordered, first up, and then down al using cell = drawerbase::listbox::cell; using export_options= drawerbase::listbox::export_options; using columns_indexs= drawerbase::listbox::size_type; + using inline_notifier_interface = drawerbase::listbox::inline_notifier_interface; public: listbox() = default; 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/menubar.hpp b/include/nana/gui/widgets/menubar.hpp index 44a4913e..8e7270bc 100644 --- a/include/nana/gui/widgets/menubar.hpp +++ b/include/nana/gui/widgets/menubar.hpp @@ -67,7 +67,6 @@ namespace nana bool _m_close_menu(); std::size_t _m_item_by_pos(const ::nana::point&); bool _m_track_mouse(const ::nana::point&); - void _m_draw(); private: widget *widget_; nana::paint::graphics *graph_; diff --git a/include/nana/gui/widgets/progress.hpp b/include/nana/gui/widgets/progress.hpp index 91e58ab7..b4f692db 100644 --- a/include/nana/gui/widgets/progress.hpp +++ b/include/nana/gui/widgets/progress.hpp @@ -30,11 +30,12 @@ namespace nana unsigned Max(unsigned); void unknown(bool); bool unknown() const; + bool stop(bool s = true); + bool stopped() const; private: void attached(widget_reference, graph_reference) override; void refresh(graph_reference) override; private: - void _m_draw(); void _m_draw_box(graph_reference); void _m_draw_progress(graph_reference); bool _m_check_changing(unsigned) const; @@ -45,6 +46,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 +69,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 stopped() const; }; }//end namespace nana #endif diff --git a/include/nana/gui/widgets/scroll.hpp b/include/nana/gui/widgets/scroll.hpp index 39491da6..54a04de0 100644 --- a/include/nana/gui/widgets/scroll.hpp +++ b/include/nana/gui/widgets/scroll.hpp @@ -19,16 +19,15 @@ namespace nana { - template class scroll; + template class scroll; //forward declaration - template struct arg_scroll : public event_arg { - scroll & widget; + window window_handle; - arg_scroll(scroll & wdg) - : widget(wdg) + arg_scroll(window wd) + : window_handle{ wd } {} }; @@ -36,11 +35,10 @@ namespace nana { namespace scroll { - template struct scroll_events : public general_events { - basic_event> value_changed; + basic_event value_changed; }; enum class buttons @@ -313,7 +311,7 @@ namespace nana private: void _m_emit_value_changed() { - widget_->events().value_changed.emit(::nana::arg_scroll(*widget_)); + widget_->events().value_changed.emit({ widget_->handle() }); } void _m_tick() @@ -335,7 +333,7 @@ namespace nana /// Provides a way to display an object which is larger than the window's client area. template class scroll // add a widget scheme? - : public widget_object, drawerbase::scroll::scroll_events> + : public widget_object, drawerbase::scroll::scroll_events> { typedef widget_object > base_type; public: @@ -448,7 +446,6 @@ namespace nana { return this->make_step(forward, range() - 1); } - };//end class scroll }//end namespace nana #endif diff --git a/include/nana/gui/widgets/skeletons/text_editor.hpp b/include/nana/gui/widgets/skeletons/text_editor.hpp index 976c168d..41812395 100644 --- a/include/nana/gui/widgets/skeletons/text_editor.hpp +++ b/include/nana/gui/widgets/skeletons/text_editor.hpp @@ -157,8 +157,8 @@ namespace nana{ namespace widgets void set_accept(std::function); void set_accept(accepts); - bool respond_char(char_type); - bool respond_key(char_type); + bool respond_char(const arg_keyboard& arg); + bool respond_key(const arg_keyboard& arg); void typeface_changed(); @@ -231,13 +231,14 @@ namespace nana{ namespace widgets void move_ns(bool to_north); //Moves up and down void move_left(); void move_right(); - nana::upoint mouse_caret(const point& screen_pos); - nana::upoint caret() const; + upoint mouse_caret(const point& screen_pos); + upoint caret() const; + point caret_screen_pos() const; bool scroll(bool upwards, bool vertical); bool mouse_enter(bool); - bool mouse_down(bool left_button, const point& screen_pos); + bool mouse_down(::nana::mouse, const point& screen_pos); bool mouse_move(bool left_button, const point& screen_pos); - bool mouse_up(bool left_button, const point& screen_pos); + bool mouse_up(::nana::mouse, const point& screen_pos); skeletons::textbase& textbase(); const skeletons::textbase& textbase() const; @@ -286,6 +287,8 @@ namespace nana{ namespace widgets unsigned _m_char_by_pixels(const nana::char_t*, std::size_t len, unsigned* pxbuf, int str_px, int pixels, bool is_rtl); unsigned _m_pixels_by_char(const nana::string&, std::size_t pos) const; static bool _m_is_right_text(const unicode_bidi::entity&); + void _handle_move_key(const arg_keyboard& arg); + private: std::unique_ptr behavior_; undoable undo_; diff --git a/include/nana/gui/widgets/skeletons/text_token_stream.hpp b/include/nana/gui/widgets/skeletons/text_token_stream.hpp index 024c12ce..eef80358 100644 --- a/include/nana/gui/widgets/skeletons/text_token_stream.hpp +++ b/include/nana/gui/widgets/skeletons/text_token_stream.hpp @@ -574,9 +574,9 @@ namespace nana{ namespace widgets{ namespace skeletons virtual void nontext_render(graph_reference graph, int x, int y) override { if(size_ != image_.size()) - image_.stretch(image_.size(), graph, nana::rectangle(x, y, size_.width, size_.height)); + image_.stretch(::nana::rectangle{ image_.size() }, graph, nana::rectangle(x, y, size_.width, size_.height)); else - image_.paste(graph, x, y); + image_.paste(graph, point{ x, y }); } virtual const nana::size & size() const override diff --git a/include/nana/gui/widgets/spinbox.hpp b/include/nana/gui/widgets/spinbox.hpp index df962c39..22b780e4 100644 --- a/include/nana/gui/widgets/spinbox.hpp +++ b/include/nana/gui/widgets/spinbox.hpp @@ -106,7 +106,7 @@ namespace nana void modifier(std::wstring prefix, std::wstring suffix); void modifier(const std::string & prefix_utf8, const std::string& suffix_utf8); private: - ::nana::string _m_caption() const; + ::nana::string _m_caption() const throw(); void _m_caption(::nana::string&&); }; //end class spinbox diff --git a/include/nana/gui/widgets/textbox.hpp b/include/nana/gui/widgets/textbox.hpp index a678d31c..85bac2a6 100644 --- a/include/nana/gui/widgets/textbox.hpp +++ b/include/nana/gui/widgets/textbox.hpp @@ -144,12 +144,15 @@ namespace nana bool saved() const; /// Read the text from a specified line. It returns true for success. - bool getline(std::size_t line_index, nana::string&) const; + bool getline(std::size_t pos, nana::string&) const; + + /// Gets the caret position + bool caret_pos(point& pos, bool text_coordinate) const; /// Appends an string. If `at_caret` is `true`, the string is inserted at the position of caret, otherwise, it is appended at end of the textbox. textbox& append(const nana::string& text, bool at_caret); - /// Determine wheter the text is line wrapped. + /// Determine wheter the text is line wrapped. bool line_wrapped() const; textbox& line_wrapped(bool); @@ -187,7 +190,7 @@ namespace nana void erase_keyword(const nana::string& kw); protected: //Overrides widget's virtual functions - ::nana::string _m_caption() const override; + ::nana::string _m_caption() const throw() override; void _m_caption(::nana::string&&) override; void _m_typeface(const paint::font&) override; }; 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/include/nana/gui/widgets/widget.hpp b/include/nana/gui/widgets/widget.hpp index 9d298fbe..18163f61 100644 --- a/include/nana/gui/widgets/widget.hpp +++ b/include/nana/gui/widgets/widget.hpp @@ -12,20 +12,26 @@ #ifndef NANA_GUI_WIDGET_HPP #define NANA_GUI_WIDGET_HPP -#include #include "../basis.hpp" #include "../programming_interface.hpp" #include #include -#include #include namespace nana { + namespace detail + { + //Forward declaration of widget_notifier_interface + class widget_notifier_interface; + } + /// Abstract class for defining the capacity interface. class widget : nana::noncopyable, nana::nonmovable { + friend class detail::widget_notifier_interface; + class notifier; typedef void(*dummy_bool_type)(widget* (*)(const widget&)); public: virtual ~widget() = default; @@ -35,9 +41,9 @@ namespace nana window parent() const; - nana::string caption() const; + nana::string caption() const throw(); void caption(std::string utf8); - void caption(nana::string); + void caption(std::wstring); template void i18n(std::string msgid, Args&&... args) @@ -70,6 +76,7 @@ namespace nana point pos() const; void move(int x, int y); + void move(const point&); void move(const rectangle&); void fgcolor(const nana::color&); @@ -88,12 +95,17 @@ namespace nana operator dummy_bool_type() const; operator window() const; + protected: + std::unique_ptr<::nana::detail::widget_notifier_interface> _m_wdg_notifier(); + private: + virtual void _m_notify_destroy() = 0; + protected: //protected members, a derived class must call this implementation if it overrides an implementation virtual void _m_complete_creation(); virtual general_events& _m_get_general_events() const = 0; - virtual nana::string _m_caption() const; + virtual nana::string _m_caption() const throw(); virtual void _m_caption(nana::string&&); virtual nana::cursor _m_cursor() const; virtual void _m_cursor(nana::cursor); @@ -152,7 +164,6 @@ namespace nana handle_ = API::dev::create_widget(parent_wd, r, this); API::dev::set_events(handle_, events_); API::dev::set_scheme(handle_, scheme_.get()); - API::dev::attach_signal(handle_, *this, &widget_object::signal); API::dev::attach_drawer(*this, trigger_); if(visible) API::show_window(handle_, true); @@ -193,29 +204,15 @@ namespace nana return trigger_; } private: - void signal(detail::signals::code code, const detail::signals& sig) - { - typedef detail::signals::code codes; - switch(code) - { - case codes::caption: - this->_m_caption(sig.info.caption); - break; - case codes::read_caption: - *sig.info.str = this->_m_caption(); - break; - case codes::destroy: - handle_ = nullptr; - break; - default: - break; - } - } - general_events& _m_get_general_events() const override { return *events_; } + + void _m_notify_destroy() override final + { + handle_ = nullptr; + } private: window handle_{nullptr}; DrawerTrigger trigger_; @@ -276,29 +273,15 @@ namespace nana return *scheme_; } private: - void signal(detail::signals::code code, const detail::signals& sig) - { - typedef detail::signals::code codes; - switch(code) - { - case codes::caption: - this->_m_caption(sig.info.caption); - break; - case codes::read_caption: - *sig.info.str = this->_m_caption(); - break; - case codes::destroy: - handle_ = nullptr; - break; - default: - break; - } - } - general_events& _m_get_general_events() const override { return *events_; } + + void _m_notify_destroy() override final + { + handle_ = nullptr; + } private: window handle_{nullptr}; std::shared_ptr events_; @@ -430,25 +413,6 @@ namespace nana return trigger_; } private: - void signal(detail::signals::code code, const detail::signals& sig) - { - typedef detail::signals::code codes; - switch(code) - { - case codes::caption: - this->_m_caption(sig.info.caption); - break; - case codes::read_caption: - *sig.info.str = this->_m_caption(); - break; - case codes::destroy: - handle_ = nullptr; - break; - default: - break; - } - } - void _m_bind_and_attach() { events_ = std::make_shared(); @@ -456,7 +420,6 @@ namespace nana scheme_ = API::dev::make_scheme(); API::dev::set_scheme(handle_, scheme_.get()); - API::dev::attach_signal(handle_, *this, &widget_object::signal); API::dev::attach_drawer(*this, trigger_); } @@ -464,6 +427,11 @@ namespace nana { return *events_; } + + void _m_notify_destroy() override final + { + handle_ = nullptr; + } private: window handle_; DrawerTrigger trigger_; @@ -511,7 +479,6 @@ namespace nana handle_ = API::dev::create_frame(parent_wd, r, this); API::dev::set_events(handle_, events_); API::dev::set_scheme(handle_, scheme_.get()); - API::dev::attach_signal(handle_, *this, &widget_object::signal); API::show_window(handle_, visible); this->_m_complete_creation(); } @@ -533,29 +500,15 @@ namespace nana return nullptr; } - void signal(detail::signals::code code, const detail::signals& sig) - { - typedef detail::signals::code codes; - switch(code) - { - case codes::caption: - this->_m_caption(sig.info.caption); - break; - case codes::read_caption: - *sig.info.str = this->_m_caption(); - break; - case codes::destroy: - handle_ = nullptr; - break; - default: - break; - } - } - general_events& _m_get_general_events() const override { return *events_; } + + void _m_notify_destroy() override final + { + handle_ = nullptr; + } private: window handle_{nullptr}; std::shared_ptr events_; diff --git a/include/nana/paint/detail/image_impl_interface.hpp b/include/nana/paint/detail/image_impl_interface.hpp index b2a7df50..6cd604fb 100644 --- a/include/nana/paint/detail/image_impl_interface.hpp +++ b/include/nana/paint/detail/image_impl_interface.hpp @@ -16,11 +16,12 @@ namespace nana{ namespace paint{ typedef nana::paint::graphics& graph_reference; virtual ~image_impl_interface() = 0; //The destructor is defined in ../image.cpp virtual bool open(const nana::char_t* filename) = 0; + virtual bool open(const void* data, std::size_t bytes) = 0; // reads image from memory virtual bool alpha_channel() const = 0; virtual bool empty() const = 0; virtual void close() = 0; virtual nana::size size() const = 0; - virtual void paste(const nana::rectangle& src_r, graph_reference dst, int x, int y) const = 0; + virtual void paste(const nana::rectangle& src_r, graph_reference dst, const point& p_dst) const = 0; virtual void stretch(const nana::rectangle& src_r, graph_reference dst, const nana::rectangle& r) const = 0; };//end class image::image_impl_interface }//end namespace paint diff --git a/include/nana/paint/graphics.hpp b/include/nana/paint/graphics.hpp index aac4712a..e790fc89 100644 --- a/include/nana/paint/graphics.hpp +++ b/include/nana/paint/graphics.hpp @@ -130,7 +130,7 @@ namespace nana void setsta(); ///< Clears the status if the graphics object had been changed void set_changed(); void release(); - void save_as_file(const char*); + void save_as_file(const char*) const; // saves image as a bitmap file void set_color(const ::nana::color&); void set_text_color(const ::nana::color&); diff --git a/include/nana/paint/image.hpp b/include/nana/paint/image.hpp index b335a4fe..b1c237bc 100644 --- a/include/nana/paint/image.hpp +++ b/include/nana/paint/image.hpp @@ -37,13 +37,16 @@ namespace paint image& operator=(const image& rhs); image& operator=(image&&); bool open(const nana::string& filename); + + /// Opens an icon from a specified buffer + bool open_icon(const void* data, std::size_t bytes); bool empty() const; operator unspecified_bool_t() const; void close(); bool alpha() const; nana::size size() const; - void paste(graphics& dst, int x, int y) const; + void paste(graphics& dst, const point& p_dst) const; void paste(const nana::rectangle& r_src, graphics& dst, const point& p_dst) const;///< Paste the area of picture specified by r_src into the destination graphics specified by dst at position p_dst. void stretch(const nana::rectangle& r_src, graphics& dst, const nana::rectangle& r_dst) const;/// + +namespace nana +{ + namespace pat + { + namespace detail + { + //A Base class for abstract factory, avoids decorated name length exceeding for a class template. + class abstract_factory_base + { + public: + virtual ~abstract_factory_base() = default; + }; + } + + template + class abstract_factory + : public detail::abstract_factory_base + { + public: + using interface_type = Interface; + + virtual ~abstract_factory() = default; + virtual std::unique_ptr create() = 0; + }; + + namespace detail + { + template + class abs_factory + : public abstract_factory + { + std::unique_ptr create() override + { + return std::unique_ptr{ new T }; + } + }; + }//end namespace detail + + template + pat::cloneable> make_factory() + { + return detail::abs_factory(); + } + }//end namespace pat +}//end namespace nana +#endif diff --git a/source/audio/detail/audio_stream.cpp b/source/audio/detail/audio_stream.cpp index 96def3d9..902911a0 100644 --- a/source/audio/detail/audio_stream.cpp +++ b/source/audio/detail/audio_stream.cpp @@ -8,17 +8,20 @@ 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); + fs_.open(static_cast(charset(file)), std::ios::binary); if(fs_) { wave_spec::master_riff_chunk riff; fs_.read(reinterpret_cast(&riff), sizeof(riff)); - if(riff.ckID == *reinterpret_cast("RIFF") && riff.waveID == *reinterpret_cast("WAVE")) + if(riff.ckID == *reinterpret_cast("RIFF") && riff.waveID == *reinterpret_cast("WAVE")) { fs_.read(reinterpret_cast(&ck_format_), sizeof(ck_format_)); - if(ck_format_.ckID == *reinterpret_cast("fmt ") && ck_format_.wFormatTag == 1) //Only support PCM format + if(ck_format_.ckID == *reinterpret_cast("fmt ") && ck_format_.wFormatTag == 1) //Only support PCM format { - std::size_t cksize = _m_locate_chunck(*reinterpret_cast("data")); + if (ck_format_.cksize > 16) + fs_.seekg(ck_format_.cksize - 16, std::ios::cur); + + std::size_t cksize = _m_locate_chunck(*reinterpret_cast("data")); if(cksize) { pcm_data_pos_ = static_cast(fs_.tellg()); @@ -77,7 +80,7 @@ namespace nana{ namespace audio if(ck.ckID == ckID) return ck.cksize; - if(ck.ckID == *reinterpret_cast("data")) + if(ck.ckID == *reinterpret_cast("data")) fs_.seekg(ck.cksize + (ck.cksize & 1 ? 1 : 0), std::ios::cur); else fs_.seekg(ck.cksize, std::ios::cur); @@ -88,4 +91,4 @@ namespace nana{ namespace audio }//end namespace detail }//end namespace audio -}//end namespace nana \ No newline at end of file +}//end namespace nana 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/include/nana/detail/linux_X11/msg_dispatcher.hpp b/source/detail/linux_X11/msg_dispatcher.hpp similarity index 99% rename from include/nana/detail/linux_X11/msg_dispatcher.hpp rename to source/detail/linux_X11/msg_dispatcher.hpp index eab77f2f..7ba64b4f 100644 --- a/include/nana/detail/linux_X11/msg_dispatcher.hpp +++ b/source/detail/linux_X11/msg_dispatcher.hpp @@ -17,7 +17,7 @@ #ifndef NANA_DETAIL_MSG_DISPATCHER_HPP #define NANA_DETAIL_MSG_DISPATCHER_HPP -#include "msg_packet.hpp" +#include #include #include #include diff --git a/source/detail/linux_X11/platform_spec.cpp b/source/detail/linux_X11/platform_spec.cpp index f53c046b..7501158d 100644 --- a/source/detail/linux_X11/platform_spec.cpp +++ b/source/detail/linux_X11/platform_spec.cpp @@ -16,7 +16,6 @@ #include #include PLATFORM_SPEC_HPP -#include #include #include #include @@ -29,6 +28,8 @@ #include #include +#include "msg_dispatcher.hpp" + namespace nana { namespace detail @@ -44,7 +45,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) @@ -922,7 +923,7 @@ namespace detail { if(crt.visible && (false == caret_reinstate(crt))) { - crt.rev_graph.bitblt(crt.size, crt.window, crt.pos); + crt.rev_graph.bitblt(rectangle{crt.size}, crt.window, crt.pos); crt.rev.width = crt.size.width; crt.rev.height = crt.size.height; crt.rev.x = crt.pos.x; @@ -948,7 +949,7 @@ namespace detail nana::paint::graphics * crt_graph; if(crt.rev.width && crt.rev.height) { - crt.rev_graph.bitblt(crt.size, root_graph, crt.pos); + crt.rev_graph.bitblt(rectangle{crt.size}, root_graph, crt.pos); crt_graph = &crt.graph; owns_caret = true; } @@ -1132,7 +1133,7 @@ namespace detail { nana::paint::graphics & graph = iconbase_[wd]; graph.make(img.size()); - img.paste(graph, 0, 0); + img.paste(graph, {}); return graph; } diff --git a/source/detail/win32/platform_spec.cpp b/source/detail/win32/platform_spec.cpp index 18b85f01..cb064545 100644 --- a/source/detail/win32/platform_spec.cpp +++ b/source/detail/win32/platform_spec.cpp @@ -273,9 +273,11 @@ namespace detail return object; } - void platform_spec::keep_window_icon(native_window_type wd, const paint::image& img) + void platform_spec::keep_window_icon(native_window_type wd, const paint::image& sml_icon, const paint::image& big_icon) { - iconbase_[wd] = img; + auto & icons = iconbase_[wd]; + icons.sml_icon = sml_icon; + icons.big_icon = big_icon; } void platform_spec::release_window_icon(native_window_type wd) diff --git a/source/filesystem/filesystem.cpp b/source/filesystem/filesystem.cpp new file mode 100644 index 00000000..51d70ff4 --- /dev/null +++ b/source/filesystem/filesystem.cpp @@ -0,0 +1,444 @@ +/* + * A ISO C++ FileSystem Implementation + * Copyright(C) 2003-2015 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.size = fst.st_size; + attr.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 static_cast(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/animation.cpp b/source/gui/animation.cpp index 9ed5a7ea..782b7c19 100644 --- a/source/gui/animation.cpp +++ b/source/gui/animation.cpp @@ -201,14 +201,14 @@ namespace nana case frame::kind::oneshot: _m_render(outs, [&frmobj](paint::graphics& tar, const nana::point& pos) { - frmobj.u.oneshot->paste(tar, pos.x, pos.y); + frmobj.u.oneshot->paste(tar, pos); }); break; case frame::kind::framebuilder: good_frame_by_frmbuilder = frmobj.u.frbuilder->frbuilder(pos_in_this_frame, framegraph, framegraph_dimension); if(good_frame_by_frmbuilder) { - nana::rectangle r = framegraph_dimension; + nana::rectangle r(framegraph_dimension); _m_render(outs, [&r, &framegraph](paint::graphics& tar, const nana::point& pos) mutable { r.x = pos.x; @@ -230,7 +230,7 @@ namespace nana switch(frmobj.type) { case frame::kind::oneshot: - frmobj.u.oneshot->paste(graph, pos.x, pos.y); + frmobj.u.oneshot->paste(graph, pos); break; case frame::kind::framebuilder: if(rebuild_frame) diff --git a/source/gui/detail/basic_window.cpp b/source/gui/detail/basic_window.cpp index 96aec1fa..6c7146eb 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,8 @@ namespace nana if(active) { native_interface::caret_create(wd_->root, size_); - real_visible_state_ = false; - visible_ = false; + + visible_state_ = visible_state::invisible; this->position(point_.x, point_.y); } else @@ -76,19 +76,26 @@ namespace nana return point_; } - void caret_descriptor::visible(bool isshow) + void caret_descriptor::visible(bool is_show) { - if(visible_ != isshow) + auto pre_displayed = (visible_state::displayed == visible_state_); + + if (is_show) { - visible_ = isshow; - if(visible_ == false || false == out_of_range_) - _m_visible(isshow); + 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_state::invisible != visible_state_); } nana::size caret_descriptor::size() const @@ -101,16 +108,8 @@ namespace nana size_ = s; update(); - if(visible_) this->visible(true); - } - - void caret_descriptor::_m_visible(bool isshow) - { - if(real_visible_state_ != isshow) - { - real_visible_state_ = isshow; - native_interface::caret_visible(wd_->root, isshow); - } + if (visible_state::invisible != visible_state_) + visible(true); } void caret_descriptor::update() @@ -138,8 +137,8 @@ namespace nana { out_of_range_ = true; - if(visible_) - _m_visible(false); + if (visible_state::invisible != visible_state_) + visible(false); } } else @@ -164,19 +163,22 @@ namespace nana if(out_of_range_) { - if(paint_size_ == size) - _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); + + visible_state_ = visible_state::invisible; + if (vs) + visible(true); + paint_size_ = size; } @@ -195,7 +197,6 @@ namespace nana { case category::root_tag::value: attribute.root = new attr_root_tag; - attribute.root->context.focus_changed = false; break; case category::frame_tag::value: attribute.frame = new attr_frame_tag; @@ -222,8 +223,8 @@ namespace nana //basic_window //@brief: constructor for the root window - basic_window::basic_window(basic_window* owner, widget* wdg, category::root_tag**) - : widget_ptr(wdg), other(category::root_tag::value) + basic_window::basic_window(basic_window* owner, std::unique_ptr&& wdg_notifier, category::root_tag**) + : widget_notifier(std::move(wdg_notifier)), other(category::root_tag::value) { drawer.bind(this); _m_init_pos_and_size(nullptr, rectangle()); @@ -282,6 +283,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) @@ -292,6 +298,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) @@ -299,6 +325,15 @@ namespace nana return false; } + basic_window * basic_window::seek_non_lite_widget_ancestor() const + { + auto anc = this->parent; + while (anc && (category::flags::lite_widget == anc->other.category)) + anc = anc->parent; + + return anc; + } + void basic_window::_m_init_pos_and_size(basic_window* parent, const rectangle& r) { pos_owner = pos_root = r; @@ -346,8 +381,9 @@ namespace nana flags.refreshing = false; flags.destroying = false; flags.borderless = false; - flags.make_bground_declared = false; - flags.ignore_menubar_focus = false; + flags.make_bground_declared = false; + flags.ignore_menubar_focus = false; + flags.ignore_mouse_focus = false; visible = false; diff --git a/source/gui/detail/bedrock_pi.cpp b/source/gui/detail/bedrock_pi.cpp index 7843fbaa..0e6c3cf8 100644 --- a/source/gui/detail/bedrock_pi.cpp +++ b/source/gui/detail/bedrock_pi.cpp @@ -26,9 +26,19 @@ namespace nana { - //class event_arg - event_arg::~event_arg(){} + //class internal_scope_guard + internal_scope_guard::internal_scope_guard() + { + detail::bedrock::instance().wd_manager.internal_lock().lock(); + } + internal_scope_guard::~internal_scope_guard() + { + detail::bedrock::instance().wd_manager.internal_lock().unlock(); + } + //end class internal_scope_guard + + //class event_arg void event_arg::stop_propagation() const { stop_propagation_ = true; @@ -52,6 +62,26 @@ namespace nana bedrock::instance().evt_operation.cancel(evt); } + class bedrock::flag_guard + { + public: + flag_guard(bedrock* brock, core_window_t * wd) + : brock_{ brock }, wd_(wd) + { + wd_->flags.refreshing = true; + } + + ~flag_guard() + { + if (brock_->wd_manager.available((wd_))) + wd_->flags.refreshing = false; + } + private: + bedrock *const brock_; + core_window_t *const wd_; + + }; + void bedrock::event_expose(core_window_t * wd, bool exposed) { if (nullptr == wd) return; @@ -63,15 +93,24 @@ 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) { - //If the wd->parent is a lite_widget then find a parent until it is not a lite_widget - wd = wd->parent; - - while (category::flags::lite_widget == wd->other.category) - wd = wd->parent; + //find an ancestor until it is not a lite_widget + wd = wd->seek_non_lite_widget_ancestor(); } else if (category::flags::frame == wd->other.category) wd = wd_manager.find_window(wd->root, wd->pos_root.x, wd->pos_root.y); @@ -91,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); } } @@ -150,9 +189,22 @@ namespace nana auto retain = wd->together.events_ptr; auto evts_ptr = retain.get(); + //enable refreshing flag, this is a RAII class for exception-safe + flag_guard fguard(this, wd); + switch (evt_code) { case event_code::click: + { + auto arg = dynamic_cast(&event_arg); + if (nullptr == arg) + return; + + wd->drawer.click(*arg); + if (!draw_only) + evts_ptr->click.emit(*arg); + } + break; case event_code::dbl_click: case event_code::mouse_enter: case event_code::mouse_move: @@ -169,10 +221,6 @@ namespace nana switch (evt_code) { - case event_code::click: - drawer_event_fn = &drawer::click; - evt_addr = &evts_ptr->click; - break; case event_code::dbl_click: drawer_event_fn = &drawer::dbl_click; evt_addr = &evts_ptr->dbl_click; @@ -202,9 +250,9 @@ namespace nana } (wd->drawer.*drawer_event_fn)(*arg); + if (!draw_only) evt_addr->emit(*arg); - break; } case event_code::mouse_wheel: diff --git a/source/gui/detail/drawer.cpp b/source/gui/detail/drawer.cpp index 3b9702a3..1cce7648 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&) + void drawer_trigger::click(graph_reference, const arg_click&) { - 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 @@ -155,7 +155,7 @@ namespace nana realizer_->typeface_changed(graphics); } - void drawer::click(const arg_mouse& arg) + void drawer::click(const arg_click& arg) { _m_emit(event_code::click, arg, &drawer_trigger::click); } @@ -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); } @@ -368,7 +364,7 @@ namespace nana void drawer::_m_bground_end() { if(core_window_->effect.bground && core_window_->effect.bground_fade_rate >= 0.01) - core_window_->other.glass_buffer.blend(core_window_->other.glass_buffer.size(), graphics, nana::point(), core_window_->effect.bground_fade_rate); + core_window_->other.glass_buffer.blend(::nana::rectangle{ core_window_->other.glass_buffer.size() }, graphics, nana::point(), core_window_->effect.bground_fade_rate); } void drawer::_m_draw_dynamic_drawing_object() diff --git a/source/gui/detail/linux_X11/bedrock.cpp b/source/gui/detail/linux_X11/bedrock.cpp index 44ccef61..3cba6d2b 100644 --- a/source/gui/detail/linux_X11/bedrock.cpp +++ b/source/gui/detail/linux_X11/bedrock.cpp @@ -23,16 +23,6 @@ namespace nana { - //class internal_scope_guard - internal_scope_guard::internal_scope_guard() - { - detail::bedrock::instance().wd_manager.internal_lock().lock(); - } - internal_scope_guard::~internal_scope_guard() - { - detail::bedrock::instance().wd_manager.internal_lock().unlock(); - } - //end class internal_scope_guard namespace detail { #pragma pack(1) @@ -138,11 +128,6 @@ namespace detail //here is the definition of this object bedrock bedrock::bedrock_object; - inline window mycast(bedrock::core_window_t* wd) - { - return reinterpret_cast(wd); - } - Window event_window(const XEvent& event) { switch(event.type) @@ -167,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. } @@ -457,62 +442,53 @@ namespace detail void assign_arg(arg_mouse& arg, basic_window* wd, unsigned msg, const XEvent& evt) { arg.window_handle = reinterpret_cast(wd); + arg.button = ::nana::mouse::any_button; + + int mask_state = 0; if (ButtonPress == msg || ButtonRelease == msg) { + mask_state = evt.xbutton.state; if (evt.xbutton.button != Button4 && evt.xbutton.button != Button5) { arg.evt_code = (ButtonPress == msg ? event_code::mouse_down : event_code::mouse_up); arg.pos.x = evt.xbutton.x - wd->pos_root.x; arg.pos.y = evt.xbutton.y - wd->pos_root.y; - arg.left_button = arg.mid_button = arg.right_button = false; - arg.shift = arg.ctrl = false; switch (evt.xbutton.button) { case Button1: - arg.left_button = true; + arg.button = ::nana::mouse::left_button; break; case Button2: - arg.mid_button = true; + arg.button = ::nana::mouse::middle_button; break; case Button3: - arg.right_button = true; + arg.button = ::nana::mouse::right_button; break; } } } else if (msg == MotionNotify) { + mask_state = evt.xmotion.state; arg.evt_code = event_code::mouse_move; arg.pos.x = evt.xmotion.x - wd->pos_root.x; arg.pos.y = evt.xmotion.y - wd->pos_root.y; - arg.left_button = arg.mid_button = arg.right_button = false; - - arg.shift = evt.xmotion.state & ShiftMask; - arg.ctrl = evt.xmotion.state & ControlMask; - if (evt.xmotion.state & Button1Mask) - arg.left_button = true; - else if (evt.xmotion.state & Button2Mask) - arg.right_button = true; - else if (evt.xmotion.state & Button3Mask) - arg.mid_button = true; } else if (EnterNotify == msg) { + mask_state = evt.xcrossing.state; arg.evt_code = event_code::mouse_enter; arg.pos.x = evt.xcrossing.x - wd->pos_root.x; arg.pos.y = evt.xcrossing.y - wd->pos_root.y; - arg.left_button = arg.mid_button = arg.right_button = false; - - arg.shift = evt.xcrossing.state & ShiftMask; - arg.ctrl = evt.xcrossing.state & ControlMask; - if (evt.xcrossing.state & Button1Mask) - arg.left_button = true; - else if (evt.xcrossing.state & Button2Mask) - arg.right_button = true; - else if (evt.xcrossing.state & Button3Mask) - arg.mid_button = true; } + + arg.left_button = ((Button1Mask & mask_state) != 0) || (::nana::mouse::left_button == arg.button) ; + arg.right_button = ((Button2Mask & mask_state) != 0) || (::nana::mouse::right_button == arg.button); + arg.mid_button = ((Button3Mask & mask_state) != 0) || (::nana::mouse::middle_button == arg.button); + arg.shift = (ShiftMask & mask_state); + arg.ctrl = (ControlMask & mask_state); + } void assign_arg(arg_focus& arg, basic_window* wd, native_window_type recv, bool getting) @@ -664,7 +640,6 @@ namespace detail auto focus = msgwnd->other.attribute.root->focus; if(focus && focus->together.caret) focus->together.caret->set_active(true); - msgwnd->root_widget->other.attribute.root->context.focus_changed = true; arg_focus arg; arg.window_handle = reinterpret_cast(focus); @@ -724,17 +699,20 @@ namespace detail bool dbl_click = (last_mouse_down_window == msgwnd) && (xevent.xbutton.time - last_mouse_down_time <= 400); last_mouse_down_time = xevent.xbutton.time; last_mouse_down_window = msgwnd; - auto new_focus = (msgwnd->flags.take_active ? msgwnd : msgwnd->other.active_window); - if(new_focus) + if (Button1 == xevent.xbutton.button) //Sets the focus only if left button is pressed { - context.event_window = new_focus; - auto kill_focus = brock.wd_manager.set_focus(new_focus, false); - if(kill_focus != new_focus) - brock.wd_manager.do_lazy_refresh(kill_focus, false); + auto new_focus = (msgwnd->flags.take_active ? msgwnd : msgwnd->other.active_window); + if (new_focus && !new_focus->flags.ignore_mouse_focus) + { + context.event_window = new_focus; + auto kill_focus = brock.wd_manager.set_focus(new_focus, false); + if (kill_focus != new_focus) + brock.wd_manager.do_lazy_refresh(kill_focus, false); + } } + auto retain = msgwnd->together.events_ptr; - msgwnd->root_widget->other.attribute.root->context.focus_changed = false; context.event_window = msgwnd; pressed_wd = nullptr; @@ -748,7 +726,7 @@ namespace detail { pressed_wd = msgwnd; //If a root window is created during the mouse_down event, Nana.GUI will ignore the mouse_up event. - if(msgwnd->root_widget->other.attribute.root->context.focus_changed) + if (msgwnd->root != native_interface::get_focus_window()) { //call the drawer mouse up event for restoring the surface graphics msgwnd->flags.action = mouse_action::normal; @@ -796,10 +774,12 @@ namespace detail bool fire_click = false; if(msgwnd == pressed_wd) { - if(msgwnd->flags.enabled && hit) + if((arg.button == ::nana::mouse::left_button) && hit) { msgwnd->flags.action = mouse_action::over; - arg.evt_code = event_code::click; + arg_click arg; + arg.window_handle = reinterpret_cast(msgwnd); + arg.by_mouse = true; emit_drawer(&drawer::click, msgwnd, arg, &context); fire_click = true; } @@ -819,7 +799,9 @@ namespace detail if(fire_click) { - arg.evt_code = event_code::click; + arg_click arg; + arg.window_handle = reinterpret_cast(msgwnd); + arg.by_mouse = true; evt_ptr->click.emit(arg); } @@ -831,7 +813,9 @@ namespace detail } else if(fire_click) { - arg.evt_code = event_code::click; + arg_click arg; + arg.window_handle = reinterpret_cast(msgwnd); + arg.by_mouse = true; msgwnd->together.events_ptr->click.emit(arg); } brock.wd_manager.do_lazy_refresh(msgwnd, false); @@ -937,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: @@ -1027,12 +1010,11 @@ namespace detail { arg_keyboard argkey; brock.get_key_state(argkey); - auto the_next = brock.wd_manager.tabstop(msgwnd, !argkey.shift); - if(the_next) + auto tstop_wd = brock.wd_manager.tabstop(msgwnd, !argkey.shift); + if (tstop_wd) { - brock.wd_manager.set_focus(the_next, false); - brock.wd_manager.do_lazy_refresh(the_next, true); - root_runtime->condition.tabstop_focus_changed = true; + brock.wd_manager.set_focus(tstop_wd, false); + brock.wd_manager.do_lazy_refresh(tstop_wd, true); } } else if(keyboard::alt == keychar) @@ -1068,6 +1050,16 @@ namespace detail arg.window_handle = reinterpret_cast(msgwnd); brock.emit(event_code::key_press, msgwnd, arg, true, &context); + if((XLookupKeySym == status) && (brock.wd_manager.available(msgwnd))) + { + //call key_char event if status is XLookupKeySym to avaid calling key_char + //twice, because the status would be equal to XLookupChars if the input method is + //enabled for the window. + arg.ignore = false; + arg.evt_code = event_code::key_char; + brock.emit(event_code::key_char, msgwnd, arg, true, & context); + } + if(msgwnd->root_widget->other.attribute.root->menubar == msgwnd) { int cmd = (menu_wd && (keyboard::escape == static_cast(arg.key)) ? 1 : 0 ); @@ -1081,6 +1073,7 @@ namespace detail break; } case XLookupChars: + if (msgwnd->flags.enabled) { const ::nana::char_t* charbuf; #if defined(NANA_UNICODE) @@ -1097,6 +1090,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; @@ -1145,7 +1142,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 < context.platform.keychar) + brock.delay_restore(2); //Restores while key release } else { diff --git a/source/gui/detail/native_window_interface.cpp b/source/gui/detail/native_window_interface.cpp index 0101a804..d706b43d 100644 --- a/source/gui/detail/native_window_interface.cpp +++ b/source/gui/detail/native_window_interface.cpp @@ -21,7 +21,7 @@ #include #endif #include - #include + #include "../../paint/detail/image_ico.hpp" #elif defined(NANA_X11) #include #include @@ -174,7 +174,7 @@ namespace nana{ } } #endif - return primary_monitor_size(); + return rectangle{ primary_monitor_size() }; } //platform-dependent @@ -509,20 +509,25 @@ namespace nana{ #endif } - bool native_interface::window_icon(native_window_type wd, const nana::paint::image& img) + bool native_interface::window_icon(native_window_type wd, const nana::paint::image& sml_icon, const ::nana::paint::image& big_icon) { #if defined(NANA_WINDOWS) - HICON ico = paint::image_accessor::icon(img); - if(ico) + HICON sml_handle = paint::image_accessor::icon(sml_icon); + HICON big_handle = paint::image_accessor::icon(big_icon); + if(sml_handle || big_handle) { - nana::detail::platform_spec::instance().keep_window_icon(wd, img); - ::SendMessage(reinterpret_cast(wd), WM_SETICON, ICON_BIG, reinterpret_cast(ico)); - ::SendMessage(reinterpret_cast(wd), WM_SETICON, ICON_SMALL, reinterpret_cast(ico)); + nana::detail::platform_spec::instance().keep_window_icon(wd, sml_icon, big_icon); + if (sml_handle) + ::SendMessage(reinterpret_cast(wd), WM_SETICON, ICON_SMALL, reinterpret_cast(sml_handle)); + + if (big_handle) + ::SendMessage(reinterpret_cast(wd), WM_SETICON, ICON_BIG, reinterpret_cast(big_handle)); return true; } #elif defined(NANA_X11) - if(wd && (false == img.empty())) + if(wd && (!sml_icon.empty() || !big_icon.empty())) { + auto & img = (sml_icon.empty() ? big_icon : sml_icon); const nana::paint::graphics & graph = restrict::spec.keep_window_icon(wd, img); XWMHints hints; @@ -537,6 +542,32 @@ namespace nana{ return false; } + /* + bool native_interface::window_icon(native_window_type wd, const paint::image& big_icon, const paint::image& small_icon) //deprecated + { +#if defined(NANA_WINDOWS) + HICON h_big_icon = paint::image_accessor::icon(big_icon); + HICON h_small_icon = paint::image_accessor::icon(small_icon); + if (h_big_icon || h_small_icon) + { + nana::detail::platform_spec::instance().keep_window_icon(wd, (!big_icon.empty() ? big_icon : small_icon)); + if (h_big_icon) { + ::SendMessage(reinterpret_cast(wd), WM_SETICON, ICON_BIG, reinterpret_cast(h_big_icon)); + } + if (h_small_icon) { + ::SendMessage(reinterpret_cast(wd), WM_SETICON, ICON_SMALL, reinterpret_cast(h_small_icon)); + } + return true; + } +#elif defined(NANA_X11) + return window_icon(wd, big_icon); +#endif + return false; + } + */ + + + void native_interface::activate_owner(native_window_type wd) { #if defined(NANA_WINDOWS) diff --git a/source/gui/detail/win32/bedrock.cpp b/source/gui/detail/win32/bedrock.cpp index 4a9d0f11..d8ac543c 100644 --- a/source/gui/detail/win32/bedrock.cpp +++ b/source/gui/detail/win32/bedrock.cpp @@ -41,16 +41,6 @@ namespace nana { void notifications_window_proc(HWND wd, WPARAM wparam, LPARAM lparam); //Defined in notifier.cpp - //class internal_scope_guard - internal_scope_guard::internal_scope_guard() - { - detail::bedrock::instance().wd_manager.internal_lock().lock(); - } - internal_scope_guard::~internal_scope_guard() - { - detail::bedrock::instance().wd_manager.internal_lock().unlock(); - } - //end class internal_scope_guard namespace detail { namespace restrict @@ -338,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) @@ -358,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 @@ -490,46 +485,63 @@ namespace detail void assign_arg(nana::arg_mouse& arg, basic_window* wd, unsigned msg, const parameter_decoder& pmdec) { arg.window_handle = reinterpret_cast(wd); - //event code + + bool set_key_state = true; switch (msg) { - case WM_LBUTTONUP: case WM_RBUTTONUP: case WM_MBUTTONUP: + case WM_LBUTTONDOWN: + arg.button = ::nana::mouse::left_button; + arg.evt_code = event_code::mouse_down; + break; + case WM_RBUTTONDOWN: + arg.button = ::nana::mouse::right_button; + arg.evt_code = event_code::mouse_down; + break; + case WM_MBUTTONDOWN: + arg.button = ::nana::mouse::middle_button; + arg.evt_code = event_code::mouse_down; + break; + case WM_LBUTTONUP: + arg.button = ::nana::mouse::left_button; arg.evt_code = event_code::mouse_up; break; - case WM_LBUTTONDBLCLK: case WM_MBUTTONDBLCLK: case WM_RBUTTONDBLCLK: + case WM_RBUTTONUP: + arg.button = ::nana::mouse::right_button; + arg.evt_code = event_code::mouse_up; + break; + case WM_MBUTTONUP: + arg.button = ::nana::mouse::middle_button; + arg.evt_code = event_code::mouse_up; + break; + case WM_LBUTTONDBLCLK: + arg.button = ::nana::mouse::left_button; + arg.evt_code = (wd->flags.dbl_click ? event_code::dbl_click : event_code::mouse_down); + break; + case WM_MBUTTONDBLCLK: + arg.button = ::nana::mouse::middle_button; + arg.evt_code = (wd->flags.dbl_click ? event_code::dbl_click : event_code::mouse_down); + break; + case WM_RBUTTONDBLCLK: + arg.button = ::nana::mouse::right_button; arg.evt_code = (wd->flags.dbl_click ? event_code::dbl_click : event_code::mouse_down); break; case WM_MOUSEMOVE: + arg.button = ::nana::mouse::any_button; arg.evt_code = event_code::mouse_move; break; + default: + set_key_state = false; } - //event arguments - switch (msg) + if (set_key_state) { - case WM_LBUTTONDOWN: case WM_RBUTTONDOWN: case WM_MBUTTONDOWN: - arg.evt_code = event_code::mouse_down; - case WM_LBUTTONUP: case WM_RBUTTONUP: case WM_MBUTTONUP: - case WM_LBUTTONDBLCLK: case WM_MBUTTONDBLCLK: case WM_RBUTTONDBLCLK: - case WM_MOUSEMOVE: arg.pos.x = pmdec.mouse.x - wd->pos_root.x; arg.pos.y = pmdec.mouse.y - wd->pos_root.y; arg.shift = pmdec.mouse.button.shift; arg.ctrl = pmdec.mouse.button.ctrl; - - switch (msg) - { - case WM_LBUTTONUP: case WM_RBUTTONUP: case WM_MBUTTONUP: - arg.left_button = (WM_LBUTTONUP == msg); - arg.right_button = (WM_RBUTTONUP == msg); - arg.mid_button = (WM_MBUTTONUP == msg); - break; - default: - arg.left_button = pmdec.mouse.button.left; - arg.mid_button = pmdec.mouse.button.middle; - arg.right_button = pmdec.mouse.button.right; - } - break; + arg.left_button = pmdec.mouse.button.left; + arg.mid_button = pmdec.mouse.button.middle; + arg.right_button = pmdec.mouse.button.right; } } @@ -591,8 +603,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: { @@ -863,8 +879,6 @@ namespace detail if(focus && focus->together.caret) focus->together.caret->set_active(true); - msgwnd->root_widget->other.attribute.root->context.focus_changed = true; - arg_focus arg; assign_arg(arg, focus, native_window, true); if (!brock.emit(event_code::focus, focus, arg, true, &context)) @@ -888,9 +902,6 @@ namespace detail //wParam indicates a handle of window that receives the focus. brock.close_menu_if_focus_other_window(reinterpret_cast(wParam)); } - //focus_changed means that during an event procedure if the focus is changed - if(brock.wd_manager.available(msgwnd)) - msgwnd->root_widget->other.attribute.root->context.focus_changed = true; def_window_proc = true; break; @@ -905,8 +916,12 @@ namespace detail msgwnd = brock.wd_manager.find_window(native_window, pmdec.mouse.x, pmdec.mouse.y); if(msgwnd && msgwnd->flags.enabled) { - if(msgwnd->flags.take_active) - brock.wd_manager.set_focus(msgwnd, false); + if (msgwnd->flags.take_active && !msgwnd->flags.ignore_mouse_focus) + { + auto killed = brock.wd_manager.set_focus(msgwnd, false); + if (killed != msgwnd) + brock.wd_manager.do_lazy_refresh(killed, false); + } arg_mouse arg; assign_arg(arg, msgwnd, message, pmdec); @@ -934,14 +949,15 @@ namespace detail if(msgwnd->flags.enabled) { pressed_wd = msgwnd; - auto new_focus = (msgwnd->flags.take_active ? msgwnd : msgwnd->other.active_window); - if(new_focus) + + if (WM_LBUTTONDOWN == message) //Sets focus only if left button is pressed { - auto kill_focus = brock.wd_manager.set_focus(new_focus, false); - if(kill_focus != new_focus) + auto new_focus = (msgwnd->flags.take_active ? msgwnd : msgwnd->other.active_window); + if (new_focus && (!new_focus->flags.ignore_mouse_focus)) { - brock.wd_manager.do_lazy_refresh(kill_focus, false); - msgwnd->root_widget->other.attribute.root->context.focus_changed = false; + auto kill_focus = brock.wd_manager.set_focus(new_focus, false); + if (kill_focus != new_focus) + brock.wd_manager.do_lazy_refresh(kill_focus, false); } } @@ -953,7 +969,7 @@ namespace detail if (brock.emit(event_code::mouse_down, msgwnd, arg, true, &context)) { //If a root_window is created during the mouse_down event, Nana.GUI will ignore the mouse_up event. - if(msgwnd->root_widget->other.attribute.root->context.focus_changed) + if (msgwnd->root != native_interface::get_focus_window()) { auto pos = native_interface::cursor_position(); auto rootwd = native_interface::find_window(pos.x, pos.y); @@ -989,15 +1005,15 @@ namespace detail nana::arg_mouse arg; assign_arg(arg, msgwnd, message, pmdec); - const bool hit = msgwnd->dimension.is_hit(arg.pos); - bool fire_click = false; - if(msgwnd == pressed_wd) + if (msgwnd->dimension.is_hit(arg.pos)) { - if(msgwnd->flags.enabled && hit) + msgwnd->flags.action = mouse_action::over; + if (::nana::mouse::left_button == arg.button) { - msgwnd->flags.action = mouse_action::over; - arg.evt_code = event_code::click; + arg_click arg; + arg.window_handle = reinterpret_cast(msgwnd); + arg.by_mouse = true; emit_drawer(&drawer::click, msgwnd, arg, &context); fire_click = true; } @@ -1006,15 +1022,14 @@ namespace detail //Do mouse_up, this handle may be closed by click handler. if(brock.wd_manager.available(msgwnd) && msgwnd->flags.enabled) { - if(hit) - msgwnd->flags.action = mouse_action::over; - arg.evt_code = event_code::mouse_up; emit_drawer(&drawer::mouse_up, msgwnd, arg, &context); if (fire_click) { - arg.evt_code = event_code::click; + arg_click arg; + arg.window_handle = reinterpret_cast(msgwnd); + arg.by_mouse = true; retain->click.emit(arg); } @@ -1026,7 +1041,9 @@ namespace detail } else if (fire_click) { - arg.evt_code = event_code::click; + arg_click arg; + arg.window_handle = reinterpret_cast(msgwnd); + arg.by_mouse = true; retain->click.emit(arg); } brock.wd_manager.do_lazy_refresh(msgwnd, false); @@ -1288,28 +1305,22 @@ namespace detail brock.event_move(msgwnd, (int)(short) LOWORD(lParam), (int)(short) HIWORD(lParam)); break; case WM_PAINT: - if (msgwnd->is_draw_through()) { - msgwnd->other.attribute.root->draw_through(); - ::PAINTSTRUCT ps; ::BeginPaint(root_window, &ps); - ::EndPaint(root_window, &ps); - } - else - { - ::PAINTSTRUCT ps; - ::HDC dc = ::BeginPaint(root_window, &ps); - - if((ps.rcPaint.left != ps.rcPaint.right) && (ps.rcPaint.bottom != ps.rcPaint.top)) + if (msgwnd->is_draw_through()) { - ::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); + msgwnd->other.attribute.root->draw_through(); + } + else + { + //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; case WM_SYSCHAR: def_window_proc = true; @@ -1391,15 +1402,16 @@ 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 { - auto the_next = brock.wd_manager.tabstop(msgwnd, (::GetKeyState(VK_SHIFT) >= 0)); - if(the_next) + bool is_forward = (::GetKeyState(VK_SHIFT) >= 0); + + auto tstop_wd = brock.wd_manager.tabstop(msgwnd, is_forward); + if (tstop_wd) { - brock.wd_manager.set_focus(the_next, false); + brock.wd_manager.set_focus(tstop_wd, false); brock.wd_manager.do_lazy_refresh(msgwnd, false); - brock.wd_manager.do_lazy_refresh(the_next, true); - root_runtime->condition.tabstop_focus_changed = true; + brock.wd_manager.do_lazy_refresh(tstop_wd, true); } } else @@ -1428,9 +1440,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; @@ -1446,8 +1459,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 @@ -1467,7 +1478,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: { @@ -1490,9 +1504,7 @@ namespace detail brock.erase_menu(false); brock.delay_restore(3); //Restores if delay_restore not decleared } - brock.wd_manager.destroy(msgwnd); - nana::detail::platform_spec::instance().release_window_icon(msgwnd->root); break; case WM_NCDESTROY: @@ -1523,15 +1535,10 @@ namespace detail return ::DefWindowProc(root_window, message, wParam, lParam); } - nana::category::flags bedrock::category(core_window_t* wd) + ::nana::category::flags bedrock::category(core_window_t* wd) { - if(wd) - { - internal_scope_guard isg; - if(wd_manager.available(wd)) - return wd->other.category; - } - return nana::category::flags::super; + internal_scope_guard lock; + return (wd_manager.available(wd) ? wd->other.category : ::nana::category::flags::super); } auto bedrock::focus() ->core_window_t* @@ -1542,13 +1549,8 @@ namespace detail native_window_type bedrock::root(core_window_t* wd) { - if(wd) - { - internal_scope_guard isg; - if(wd_manager.available(wd)) - return wd->root; - } - return nullptr; + internal_scope_guard lock; + return (wd_manager.available(wd) ? wd->root : nullptr); } void bedrock::set_menubar_taken(core_window_t* wd) diff --git a/source/gui/detail/window_layout.cpp b/source/gui/detail/window_layout.cpp index 99ab04ab..c2edb173 100644 --- a/source/gui/detail/window_layout.cpp +++ b/source/gui/detail/window_layout.cpp @@ -1,7 +1,7 @@ /* * Window Layout Implementation * Nana C++ Library(http://www.nanapro.org) -* Copyright(C) 2003-2014 Jinhao(cnjinhao@hotmail.com) +* Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -24,12 +24,13 @@ namespace nana //class window_layout void window_layout::paint(core_window_t* wd, bool is_redraw, bool is_child_refreshed) { + if (wd->flags.refreshing || wd->drawer.graphics.empty()) + return; + if (nullptr == wd->effect.bground) { if (is_redraw) { - if (wd->flags.refreshing) return; - wd->flags.refreshing = true; wd->drawer.refresh(); wd->flags.refreshing = false; @@ -42,6 +43,10 @@ namespace nana bool window_layout::maproot(core_window_t* wd, bool have_refreshed, bool is_child_refreshed) { + auto check_opaque = wd->seek_non_lite_widget_ancestor(); + if (check_opaque && check_opaque->flags.refreshing) + return true; + nana::rectangle vr; if (read_visual_rectangle(wd, vr)) { @@ -56,7 +61,6 @@ namespace nana if (wd->parent) { std::vector blocks; - blocks.reserve(10); if (read_overlaps(wd, vr, blocks)) { nana::point p_src; @@ -96,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 }; @@ -205,7 +209,7 @@ namespace nana beg = beg->parent; } - glass_buffer.bitblt(wd->dimension, beg->drawer.graphics, wd->pos_root - beg->pos_root); + glass_buffer.bitblt(::nana::rectangle{ wd->dimension }, beg->drawer.graphics, wd->pos_root - beg->pos_root); nana::rectangle r(wd->pos_owner, wd->dimension); for (auto i = layers.rbegin(), layers_rend = layers.rend(); i != layers_rend; ++i) @@ -235,7 +239,7 @@ namespace nana } } else - glass_buffer.bitblt(wd->dimension, wd->parent->drawer.graphics, wd->pos_owner); + glass_buffer.bitblt(::nana::rectangle{ wd->dimension }, wd->parent->drawer.graphics, wd->pos_owner); const rectangle r_of_wd{ wd->pos_owner, wd->dimension }; for (auto child : wd->parent->children) @@ -351,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 b7719985..6cfabb94 100644 --- a/source/gui/detail/window_manager.cpp +++ b/source/gui/detail/window_manager.cpp @@ -1,7 +1,7 @@ /* * Window Manager Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2014 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -42,8 +42,8 @@ namespace detail { root_register misc_register; handle_manager wd_register; - signal_manager signal; - paint::image default_icon; + paint::image default_icon_big; + paint::image default_icon_small; }; //end struct wdm_private_impl @@ -171,22 +171,6 @@ namespace detail impl_->wd_register.all(v); } - void window_manager::signal_fire_caption(core_window_t* wd, const nana::char_t* str) - { - detail::signals sig; - sig.info.caption = str; - impl_->signal.call_signal(wd, signals::code::caption, sig); - } - - nana::string window_manager::signal_fire_caption(core_window_t* wd) - { - nana::string str; - detail::signals sig; - sig.info.str = &str; - impl_->signal.call_signal(wd, signals::code::read_caption, sig); - return str; - } - void window_manager::event_filter(core_window_t* wd, bool is_make, event_code evtid) { switch(evtid) @@ -199,11 +183,6 @@ namespace detail } } - void window_manager::default_icon(const paint::image& img) - { - impl_->default_icon = img; - } - bool window_manager::available(core_window_t* wd) { return impl_->wd_register.available(wd); @@ -224,10 +203,10 @@ namespace detail return false; } - window_manager::core_window_t* window_manager::create_root(core_window_t* owner, bool nested, rectangle r, const appearance& app, widget * wdg) + window_manager::core_window_t* window_manager::create_root(core_window_t* owner, bool nested, rectangle r, const appearance& app, widget* wdg) { native_window_type native = nullptr; - if(owner) + if (owner) { //Thread-Safe Required! std::lock_guard lock(mutex_); @@ -244,9 +223,9 @@ namespace detail } auto result = native_interface::create_window(native, nested, r, app); - if(result.native_handle) + if (result.native_handle) { - core_window_t* wd = new core_window_t(owner, wdg, (category::root_tag**)nullptr); + core_window_t* wd = new core_window_t(owner, widget_notifier_interface::get_notifier(wdg), (category::root_tag**)nullptr); wd->flags.take_active = !app.no_activate; wd->title = native_interface::window_caption(result.native_handle); @@ -260,11 +239,11 @@ namespace detail wd->bind_native_window(result.native_handle, result.width, result.height, result.extra_width, result.extra_height, value->root_graph); impl_->wd_register.insert(wd, wd->thread_id); - if(owner && owner->other.category == category::frame_tag::value) + if (owner && owner->other.category == category::frame_tag::value) insert_frame(owner, wd); bedrock::inc_window(wd->thread_id); - this->icon(wd, impl_->default_icon); + this->icon(wd, impl_->default_icon_small, impl_->default_icon_big); return wd; } return nullptr; @@ -277,7 +256,7 @@ namespace detail if (impl_->wd_register.available(parent) == false) return nullptr; - core_window_t * wd = new core_window_t(parent, r, wdg, (category::frame_tag**)nullptr); + core_window_t * wd = new core_window_t(parent, widget_notifier_interface::get_notifier(wdg), r, (category::frame_tag**)nullptr); wd->frame_window(native_interface::create_child_window(parent->root, rectangle(wd->pos_root.x, wd->pos_root.y, r.width, r.height))); impl_->wd_register.insert(wd, wd->thread_id); @@ -324,11 +303,13 @@ namespace detail if (impl_->wd_register.available(parent) == false) throw std::invalid_argument("invalid parent/owner handle"); + auto wdg_notifier = widget_notifier_interface::get_notifier(wdg); + core_window_t * wd; - if(is_lite) - wd = new core_window_t(parent, r, wdg, (category::lite_widget_tag**)nullptr); + if (is_lite) + wd = new core_window_t(parent, std::move(wdg_notifier), r, (category::lite_widget_tag**)nullptr); else - wd = new core_window_t(parent, r, wdg, (category::widget_tag**)nullptr); + wd = new core_window_t(parent, std::move(wdg_notifier), r, (category::widget_tag**)nullptr); impl_->wd_register.insert(wd, wd->thread_id); return wd; } @@ -358,8 +339,8 @@ namespace detail //before the window_manager destroyes the window, and then, window_manager detaches the //non-existing drawer_trigger which is destroyed by destruction of widget. Crash! wd->drawer.detached(); - impl_->signal.call_signal(wd, signals::code::destroy, signals_); - impl_->signal.umake(wd); + + wd->widget_notifier->destroy(); native_interface::close_window(wd->root); } @@ -372,21 +353,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 @@ -404,15 +390,21 @@ namespace detail } } - void window_manager::icon(core_window_t* wd, const paint::image& img) + void window_manager::default_icon(const nana::paint::image& small, const nana::paint::image& big) { - if(false == img.empty()) + impl_->default_icon_big = big; + impl_->default_icon_small = small; + } + + void window_manager::icon(core_window_t* wd, const paint::image& small_icon, const paint::image& big_icon) + { + if(!big_icon.empty() || !small_icon.empty()) { std::lock_guard lock(mutex_); if (impl_->wd_register.available(wd)) { if(wd->other.category == category::root_tag::value) - native_interface::window_icon(wd->root, img); + native_interface::window_icon(wd->root, small_icon, big_icon); } } } @@ -423,35 +415,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) @@ -489,9 +480,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); @@ -518,7 +506,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) @@ -529,9 +517,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; @@ -640,7 +625,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); } } } @@ -672,7 +657,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_); @@ -680,12 +665,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 } } @@ -694,26 +679,28 @@ 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())) { - wndlayout_type::paint(wd, redraw, false); - this->map(wd, forced); - } - else - { - if(redraw) - wndlayout_type::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; + if (!wd->flags.refreshing) + { + window_layer::paint(wd, redraw, false); + this->map(wd, forced, update_area); + return true; + } } + 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; } return true; } @@ -724,8 +711,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 @@ -746,18 +733,16 @@ 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); + 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; @@ -777,7 +762,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; } @@ -786,14 +771,14 @@ 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); } ::nana::widget* window_manager::get_widget(core_window_t* wd) const { std::lock_guard lock(mutex_); - return (impl_->wd_register.available(wd) ? wd->widget_ptr : nullptr); + return (impl_->wd_register.available(wd) ? wd->widget_notifier->widget_ptr() : nullptr); } std::vector window_manager::get_children(core_window_t* wd) const @@ -933,6 +918,9 @@ namespace detail // the return value is NULL window_manager::core_window_t* window_manager::capture_window(core_window_t* wd, bool value) { + if (!this->available(wd)) + return nullptr; + nana::point pos = native_interface::cursor_position(); auto & attr_cap = attr_.capture.history; @@ -1015,6 +1003,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! @@ -1023,33 +1044,32 @@ namespace detail return nullptr; auto & tabs = wd->root_widget->other.attribute.root->tabstop; - if (tabs.size()) + if (tabs.empty()) + return nullptr; + + bool precondition = false; + for (auto & tab_wd : tabs) { - if (forward) // + if (tab_wd->displayed()) { - 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; - 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 is required when moving perviously. - { - auto i = std::find(tabs.cbegin(), tabs.cend(), wd); - if (i != tabs.cend()) - return (tabs.cbegin() == i ? tabs.back() : *(i - 1)); + precondition = true; + break; } } + + 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; } @@ -1063,7 +1083,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; } @@ -1161,11 +1181,6 @@ namespace detail return nullptr; } - void window_manager::_m_attach_signal(core_window_t* wd, signal_invoker_interface* si) - { - impl_->signal.make(wd, si); - } - bool check_tree(basic_window* wd, basic_window* const cond) { if (wd == cond) return true; @@ -1252,18 +1267,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) { @@ -1363,21 +1369,22 @@ namespace detail delete wd->together.caret; wd->together.caret = nullptr; } - //Delete the children widgets. - for (auto i = wd->children.rbegin(), end = wd->children.rend(); i != end; ++i) - _m_destroy(*i); - wd->children.clear(); arg_destroy arg; arg.window_handle = reinterpret_cast(wd); brock.emit(event_code::destroy, wd, arg, true, brock.get_thread_context()); + //Delete the children widgets. + for (auto i = wd->children.rbegin(), end = wd->children.rend(); i != end; ++i) + _m_destroy(*i); + wd->children.clear(); + + _m_disengage(wd, nullptr); - wndlayout_type::enable_effects_bground(wd, false); + window_layer::enable_effects_bground(wd, false); wd->drawer.detached(); - impl_->signal.call_signal(wd, signals::code::destroy, signals_); - impl_->signal.umake(wd); + wd->widget_notifier->destroy(); if(wd->other.category == category::frame_tag::value) { @@ -1398,9 +1405,17 @@ 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); + if (wd->displayed() && wd->effect.bground) + window_layer::make_bground(wd); + for (auto child : wd->children) _m_move_core(child, delta); } diff --git a/source/gui/effects.cpp b/source/gui/effects.cpp index 3570fa21..7ab15482 100644 --- a/source/gui/effects.cpp +++ b/source/gui/effects.cpp @@ -26,7 +26,7 @@ namespace nana { if(fade_rate_ < 0.001) return; - graph.blend(graph.size(), API::bgcolor(wd), fade_rate_); + graph.blend(::nana::rectangle{ graph.size() }, API::bgcolor(wd), fade_rate_); } private: const double fade_rate_; @@ -42,7 +42,7 @@ namespace nana void take_effect(window, graph_reference graph) const { - graph.blur(graph.size(), radius_); + graph.blur(::nana::rectangle{ graph.size() }, radius_); } private: const std::size_t radius_; diff --git a/source/gui/element.cpp b/source/gui/element.cpp index 71d70f8b..1852a554 100644 --- a/source/gui/element.cpp +++ b/source/gui/element.cpp @@ -141,7 +141,7 @@ namespace nana } else { - ::nana::color highlighted(0x5e, 0xb6, 0xf7); + ::nana::color highlighted(static_cast(0x5eb6f7)); auto bld_bgcolor = bgcolor; auto bld_fgcolor = fgcolor; switch(es) @@ -156,7 +156,7 @@ namespace nana bld_fgcolor = fgcolor.blend(highlighted, 0.4); break; case element_state::disabled: - bld_bgcolor = bld_fgcolor = nana::color(0xb2, 0xb7, 0xbc); + bld_bgcolor = bld_fgcolor.from_rgb(0xb2, 0xb7, 0xbc); break; default: //Leave things as they are @@ -264,7 +264,7 @@ namespace nana bool draw(graph_reference graph, const ::nana::color& bgcolor, const ::nana::color& fgcolor, const ::nana::rectangle& r, element_state estate, unsigned weight) { graph.rectangle(r, false, static_cast((element_state::focus_hovered == estate || element_state::focus_normal == estate) ? 0x0595E2 : 0x999A9E)); - graph.rectangle(::nana::rectangle{r}.pare_off(1), false, bgcolor); + graph.rectangle(::nana::rectangle(r).pare_off(1), false, bgcolor); return true; } }; @@ -580,9 +580,6 @@ namespace nana class element_manager : nana::noncopyable, nana::nonmovable { - //VC2012 does not support alias declaration. - //template using factory_interface = element::provider::factory_interface; - template struct item { diff --git a/source/gui/filebox.cpp b/source/gui/filebox.cpp index 14ab8b67..441d5682 100644 --- a/source/gui/filebox.cpp +++ b/source/gui/filebox.cpp @@ -163,7 +163,7 @@ namespace nana btn_folder_.create(*this); btn_folder_.caption(STR("&New Folder")); - btn_folder_.events().click.connect_unignorable([this](const arg_mouse&) + btn_folder_.events().click.connect_unignorable([this](const arg_click&) { form fm(this->handle(), API::make_center(*this, 300, 35)); fm.caption(STR("Name the new folder")); @@ -179,7 +179,7 @@ namespace nana button btn_cancel(fm, nana::rectangle(235, 5, 60, 25)); btn_cancel.caption(STR("Cancel")); - btn_cancel.events().click.connect_unignorable([&fm](const arg_mouse&) + btn_cancel.events().click.connect_unignorable([&fm](const arg_click&) { fm.close(); }); @@ -288,14 +288,14 @@ namespace nana btn_ok_.create(*this); btn_ok_.caption(STR("&OK")); - btn_ok_.events().click.connect_unignorable([this](const arg_mouse&) + btn_ok_.events().click.connect_unignorable([this](const arg_click&) { _m_ok(); }); btn_cancel_.create(*this); btn_cancel_.caption(STR("&Cancel")); - btn_cancel_.events().click.connect_unignorable([this](const arg_mouse&) + btn_cancel_.events().click.connect_unignorable([this](const arg_click&) { API::close_window(handle()); }); @@ -1021,6 +1021,7 @@ namespace nana if (!impl_->open_or_save) ofn.Flags = OFN_OVERWRITEPROMPT; //Overwrite prompt if it is save mode + ofn.Flags |= OFN_NOCHANGEDIR; if(FALSE == (impl_->open_or_save ? ::GetOpenFileName(&ofn) : ::GetSaveFileName(&ofn))) return false; diff --git a/source/gui/layout_utility.cpp b/source/gui/layout_utility.cpp index 8ab4c9b9..c81273b3 100644 --- a/source/gui/layout_utility.cpp +++ b/source/gui/layout_utility.cpp @@ -52,11 +52,13 @@ namespace nana bool overlap(const rectangle& ir, const size& valid_input_area, const rectangle & dr, const size& valid_dst_area, rectangle& op_ir, rectangle& op_dr) { - if(overlap(ir, valid_input_area, op_ir) == false) + rectangle valid_r{ valid_input_area }; + if (overlap(ir, valid_r, op_ir) == false) return false; + valid_r = valid_dst_area; rectangle good_dr; - if(overlap(dr, valid_dst_area, good_dr) == false) + if (overlap(dr, valid_r, good_dr) == false) return false; zoom(ir, op_ir, dr, op_dr); diff --git a/source/gui/msgbox.cpp b/source/gui/msgbox.cpp index de471567..c514f124 100644 --- a/source/gui/msgbox.cpp +++ b/source/gui/msgbox.cpp @@ -48,7 +48,7 @@ namespace nana { graph.rectangle(rectangle{0, 0, graph.width(), graph.height() - 50}, true, colors::white); if(ico_.empty() == false) - ico_.stretch(ico_.size(), graph, rectangle{12, 25, 32, 32}); + ico_.stretch(rectangle{ico_.size()}, graph, rectangle{12, 25, 32, 32}); }); unsigned width_pixel = 45; @@ -57,7 +57,7 @@ namespace nana place_.bind(*this); yes_.create(*this); - yes_.events().click.connect_unignorable([this](const arg_mouse& arg) + yes_.events().click.connect_unignorable([this](const arg_click& arg) { _m_click(arg); }); @@ -69,7 +69,7 @@ namespace nana yes_.caption(STR("Yes")); no_.create(*this); no_.caption(STR("No")); - no_.events().click.connect_unignorable([this](const arg_mouse& arg) + no_.events().click.connect_unignorable([this](const arg_click& arg) { _m_click(arg); }); @@ -80,7 +80,7 @@ namespace nana { cancel_.create(*this); cancel_.caption(STR("Cancel")); - cancel_.events().click.connect_unignorable([this](const arg_mouse& arg) + cancel_.events().click.connect_unignorable([this](const arg_click& arg) { _m_click(arg); }); @@ -303,11 +303,11 @@ namespace nana nana::paint::pixel_buffer pxbuf(32, 32); pxbuf.put(reinterpret_cast(rawpx), 32, 32, 32, 4*32, true); ico_.make({32, 32}); - pxbuf.paste(ico_.handle(), 0, 0); + pxbuf.paste(ico_.handle(), {}); } } - void _m_click(const arg_mouse& arg) + void _m_click(const arg_click& arg) { if(arg.window_handle == yes_) pick_ = (no_.empty() ? msgbox::pick_ok : msgbox::pick_yes); diff --git a/source/gui/programming_interface.cpp b/source/gui/programming_interface.cpp index d6f8b483..6a64dc2d 100644 --- a/source/gui/programming_interface.cpp +++ b/source/gui/programming_interface.cpp @@ -112,7 +112,7 @@ namespace API iwd->effect.bground = new_effect_ptr; iwd->effect.bground_fade_rate = fade_rate; restrict::window_manager.enable_effects_bground(iwd, true); - + if (fade_rate < 0.01) iwd->flags.make_bground_declared = true; @@ -184,7 +184,7 @@ namespace API } } - nana::string window_caption(window wd) + nana::string window_caption(window wd) throw() { auto const iwd = reinterpret_cast(wd); internal_scope_guard isg; @@ -354,14 +354,14 @@ namespace API return r; } - void window_icon_default(const paint::image& img) + void window_icon_default(const paint::image& small_icon, const paint::image& big_icon) { - restrict::window_manager.default_icon(img); + restrict::window_manager.default_icon(small_icon, big_icon); } - void window_icon(window wd, const paint::image& img) + void window_icon(window wd, const paint::image& small_icon, const paint::image& big_icon) { - restrict::window_manager.icon(reinterpret_cast(wd), img); + restrict::window_manager.icon(reinterpret_cast(wd), small_icon, big_icon); } bool empty_window(window wd) @@ -374,6 +374,16 @@ namespace API return restrict::window_manager.available(reinterpret_cast(wd)); } + bool is_destroying(window wd) + { + auto iwd = reinterpret_cast(wd); + internal_scope_guard lock; + if (!restrict::window_manager.available(iwd)) + return false; + + return iwd->flags.destroying; + } + void enable_dropfiles(window wd, bool enb) { internal_scope_guard lock; @@ -534,13 +544,19 @@ namespace API internal_scope_guard lock; if(restrict::window_manager.move(iwd, x, y, false)) { - if(category::flags::root != iwd->other.category) + restrict::core_window_t* update_wd = nullptr; + if (iwd->displayed() && iwd->effect.bground) { - do{ - iwd = iwd->parent; - } while (category::flags::lite_widget == iwd->other.category); + update_wd = iwd; + restrict::window_manager.update(iwd, true, false); } - restrict::window_manager.update(iwd, false, false); + + restrict::core_window_t* anc = iwd; + if (category::flags::root != iwd->other.category) + anc = iwd->seek_non_lite_widget_ancestor(); + + if (anc != update_wd) + restrict::window_manager.update(anc, false, false); } } @@ -551,11 +567,8 @@ namespace API if(restrict::window_manager.move(iwd, r)) { if (category::flags::root != iwd->other.category) - { - do{ - iwd = iwd->parent; - } while (category::flags::lite_widget == iwd->other.category); - } + iwd = iwd->seek_non_lite_widget_ancestor(); + restrict::window_manager.update(iwd, false, false); } } @@ -624,11 +637,8 @@ namespace API if(restrict::window_manager.size(iwd, sz, false, false)) { if (category::flags::root != iwd->other.category) - { - do{ - iwd = iwd->parent; - } while (category::flags::lite_widget == iwd->other.category); - } + iwd = iwd->seek_non_lite_widget_ancestor(); + restrict::window_manager.update(iwd, false, false); } } @@ -639,7 +649,7 @@ namespace API internal_scope_guard lock; if (!restrict::window_manager.available(iwd)) return{}; - + auto sz = window_size(wd); sz.width += iwd->extra_width; sz.height += iwd->extra_height; @@ -721,7 +731,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); } @@ -770,16 +780,16 @@ namespace API { auto const iwd = reinterpret_cast(wd); internal_scope_guard lock; - if(restrict::window_manager.available(iwd)) - restrict::window_manager.signal_fire_caption(iwd, title.c_str()); + if (restrict::window_manager.available(iwd)) + iwd->widget_notifier->caption(title); } - + nana::string window_caption(window wd) { auto const iwd = reinterpret_cast(wd); internal_scope_guard lock; - if(restrict::window_manager.available(iwd)) - return restrict::window_manager.signal_fire_caption(iwd); + if (restrict::window_manager.available(iwd)) + return iwd->widget_notifier->caption(); return{}; } @@ -930,7 +940,7 @@ namespace API if (prev != clr) { iwd->scheme->background = clr; - + //If the bground mode of this window is basic, it should remake the background if (iwd->effect.bground && iwd->effect.bground_fade_rate < 0.01) // fade rate < 0.01 means it is basic mode iwd->flags.make_bground_declared = true; @@ -1284,5 +1294,25 @@ namespace API } return nana::element_state::normal; } + + bool ignore_mouse_focus(window wd, bool ignore) + { + auto iwd = reinterpret_cast(wd); + internal_scope_guard lock; + if (restrict::window_manager.available(iwd)) + { + auto state = iwd->flags.ignore_mouse_focus; + iwd->flags.ignore_mouse_focus = ignore; + return state; + } + return false; + } + + bool ignore_mouse_focus(window wd) + { + auto iwd = reinterpret_cast(wd); + internal_scope_guard lock; + return (restrict::window_manager.available(iwd) ? iwd->flags.ignore_mouse_focus : false); + } }//end namespace API }//end namespace nana diff --git a/source/gui/screen.cpp b/source/gui/screen.cpp index f94b0dfa..1a0be98b 100644 --- a/source/gui/screen.cpp +++ b/source/gui/screen.cpp @@ -114,7 +114,7 @@ namespace nana void load_monitors() { displays.clear(); - displays.emplace_back(0, primary_monitor_size()); + displays.emplace_back(0, rectangle{primary_monitor_size()}); } #endif diff --git a/source/gui/widgets/button.cpp b/source/gui/widgets/button.cpp index 63d8e810..e68d9a3b 100644 --- a/source/gui/widgets/button.cpp +++ b/source/gui/widgets/button.cpp @@ -17,6 +17,7 @@ namespace nana{ namespace drawerbase { namespace button { + //trigger //@brief: draw the button trigger::trigger() @@ -97,14 +98,41 @@ namespace nana{ namespace drawerbase void trigger::refresh(graph_reference graph) { - _m_draw(graph); + bool eb = wdg_->enabled();; + + attr_.bgcolor = wdg_->bgcolor(); + attr_.fgcolor = wdg_->fgcolor(); + + element_state e_state = attr_.e_state; + if (eb) + { + if (attr_.focused) + { + if (element_state::normal == e_state) + e_state = element_state::focus_normal; + else if (element_state::hovered == e_state) + e_state = element_state::focus_hovered; + } + } + else + e_state = element_state::disabled; + + if (false == cite_.draw(graph, attr_.bgcolor, attr_.fgcolor, ::nana::rectangle{ graph.size() }, e_state)) + { + if (bground_mode::basic != API::effects_bground_mode(wdg_->handle())) + { + _m_draw_background(graph); + _m_draw_border(graph); + } + } + _m_draw_title(graph, eb); } void trigger::mouse_enter(graph_reference graph, const arg_mouse&) { attr_.e_state = (attr_.pushed || attr_.keep_pressed ? element_state::pressed : element_state::hovered); - _m_draw(graph); + refresh(graph); API::lazy_refresh(); } @@ -114,49 +142,39 @@ namespace nana{ namespace drawerbase return; attr_.e_state = element_state::normal; - _m_draw(graph); + refresh(graph); API::lazy_refresh(); } - void trigger::mouse_down(graph_reference graph, const arg_mouse&) + void trigger::mouse_down(graph_reference graph, const arg_mouse& arg) { - attr_.e_state = element_state::pressed; - attr_.keep_pressed = true; + if (::nana::mouse::left_button != arg.button) + return; - _m_draw(graph); - API::capture_window(*wdg_, true); - API::lazy_refresh(); + _m_press(graph, true); } - void trigger::mouse_up(graph_reference graph, const arg_mouse&) + void trigger::mouse_up(graph_reference graph, const arg_mouse& arg) { - API::capture_window(*wdg_, false); - attr_.keep_pressed = false; - if(attr_.enable_pushed && (false == attr_.pushed)) - { - attr_.pushed = true; - } - else - { - if (element_state::pressed == attr_.e_state) - attr_.e_state = element_state::hovered; - else - attr_.e_state = element_state::normal; + if (::nana::mouse::left_button != arg.button) + return; - attr_.pushed = false; - _m_draw(graph); - API::lazy_refresh(); - } + _m_press(graph, false); } void trigger::key_char(graph_reference, const arg_keyboard& arg) { - if(arg.key == static_cast(keyboard::enter)) + if (static_cast(keyboard::enter) == arg.key) emit_click(); } - void trigger::key_press(graph_reference, const arg_keyboard& arg) + void trigger::key_press(graph_reference graph, const arg_keyboard& arg) { + if (keyboard::space == static_cast(arg.key)) + { + _m_press(graph, true); + return; + } bool ch_tabstop_next; switch(arg.key) { @@ -169,13 +187,26 @@ namespace nana{ namespace drawerbase default: return; } + API::move_tabstop(*wdg_, ch_tabstop_next); } + void trigger::key_release(graph_reference graph, const arg_keyboard& arg) + { + if (arg.key != static_cast(keyboard::space)) + return; + + emit_click(); + + //Check the widget, because it may be deleted by click event + if (API::is_window(*wdg_)) + _m_press(graph, false); + } + void trigger::focus(graph_reference graph, const arg_focus& arg) { attr_.focused = arg.getting; - _m_draw(graph); + refresh(graph); API::lazy_refresh(); } @@ -217,12 +248,8 @@ namespace nana{ namespace drawerbase ++pos.x; ++pos.y; } - //color_t fgcolor = (attr_.focus_color ? (attr_.focused ? 0xFF : attr_.fgcolor) : attr_.fgcolor); - auto fgcolor = attr_.fgcolor; - if (attr_.focus_color && attr_.focused) - fgcolor = ::nana::color(colors::blue); - graph.set_text_color(fgcolor); + graph.set_text_color(attr_.focus_color && attr_.focused ? ::nana::color(colors::blue) : attr_.fgcolor); if (attr_.omitted) tr.render(pos, txtptr, txtlen, omitted_pixels, true); @@ -258,39 +285,7 @@ namespace nana{ namespace drawerbase } if(attr_.icon) - attr_.icon->paste(graph, 3, (gsize.height - icon_sz.height) / 2); - } - - void trigger::_m_draw(graph_reference graph) - { - bool eb = wdg_->enabled();; - - attr_.bgcolor = wdg_->bgcolor(); - attr_.fgcolor = wdg_->fgcolor(); - - element_state e_state = attr_.e_state; - if (eb) - { - if (attr_.focused) - { - if (element_state::normal == e_state) - e_state = element_state::focus_normal; - else if (element_state::hovered == e_state) - e_state = element_state::focus_hovered; - } - } - else - e_state = element_state::disabled; - - if (false == cite_.draw(graph, attr_.bgcolor, attr_.fgcolor, graph.size(), e_state)) - { - if (bground_mode::basic != API::effects_bground_mode(wdg_->handle())) - { - _m_draw_background(graph); - _m_draw_border(graph); - } - } - _m_draw_title(graph, eb); + attr_.icon->paste(graph, point{ 3, static_cast(gsize.height - icon_sz.height) / 2 }); } void trigger::_m_draw_background(graph_reference graph) @@ -309,40 +304,76 @@ namespace nana{ namespace drawerbase graph.gradual_rectangle(r, from, to, true); } + void draw_corner_point(::nana::paint::graphics& graph, const rectangle& r) + { + graph.set_pixel(r.x, r.y); + graph.set_pixel(r.right() - 1, r.y); + graph.set_pixel(r.right() - 1, r.bottom() - 1); + graph.set_pixel(r.x, r.bottom() - 1); + } + void trigger::_m_draw_border(graph_reference graph) { nana::rectangle r(graph.size()); - int right = r.width - 1; - int bottom = r.height - 1; - ::nana::color lt{0x7f,0x7f,0x7f}, rb{0x70,0x70,0x70}; + ::nana::color lt(static_cast(0x7f7f7f)), rb(static_cast(0x707070)); graph.frame_rectangle(r, lt, lt, rb, rb); - graph.set_color({0x91,0x91,0x91}); - graph.set_pixel(1, 1); - graph.set_pixel(right - 1, 1); - graph.set_pixel(right - 1, bottom - 1); - graph.set_pixel(1, bottom - 1); - graph.set_color(colors::button_face); - graph.set_pixel(0, 0); - graph.set_pixel(right, 0); - graph.set_pixel(0, bottom); - graph.set_pixel(right, bottom); + draw_corner_point(graph, r); + + graph.set_color(static_cast(0x919191)); + draw_corner_point(graph, r.pare_off(1)); if (element_state::pressed == attr_.e_state) - graph.rectangle(r.pare_off(1), false, {0xc3, 0xc3, 0xc3}); + graph.rectangle(r, false, static_cast(0xc3c3c3)); + } + + void trigger::_m_press(graph_reference graph, bool is_pressed) + { + bool draw = false; + if (is_pressed) + { + if (attr_.e_state != element_state::pressed) + { + attr_.e_state = element_state::pressed; + attr_.keep_pressed = true; + API::capture_window(*wdg_, true); + draw = true; + } + } + else + { + API::capture_window(*wdg_, false); + attr_.keep_pressed = false; + if (attr_.enable_pushed && (false == attr_.pushed)) + { + attr_.pushed = true; + } + else + { + if (element_state::pressed == attr_.e_state) + attr_.e_state = element_state::hovered; + else + attr_.e_state = element_state::normal; + + attr_.pushed = false; + draw = true; + } + } + + if (draw) + { + refresh(graph); + API::lazy_refresh(); + } } void trigger::emit_click() { - arg_mouse arg; - arg.evt_code = event_code::click; + arg_click arg; arg.window_handle = wdg_->handle(); - arg.ctrl = arg.shift = false; - arg.mid_button = arg.right_button = false; - arg.left_button = true; - arg.pos.x = arg.pos.y = 1; + arg.by_mouse = false; API::emit_event(event_code::click, arg.window_handle, arg); } @@ -470,18 +501,11 @@ namespace nana{ namespace drawerbase return *this; } - - - void button::_m_shortkey() - { - get_drawer_trigger().emit_click(); - } - void button::_m_complete_creation() { events().shortkey.connect_unignorable([this] { - _m_shortkey(); + get_drawer_trigger().emit_click(); }); } diff --git a/source/gui/widgets/categorize.cpp b/source/gui/widgets/categorize.cpp index e891afea..4351a719 100644 --- a/source/gui/widgets/categorize.cpp +++ b/source/gui/widgets/categorize.cpp @@ -156,10 +156,10 @@ namespace nana { rectangle r{ graph.size() }; - graph.rectangle(r, false, { 0xf0, 0xf0, 0xf0 }); + graph.rectangle(r, false, static_cast(0xf0f0f0)); - color lb(0x9d, 0xab, 0xb9); - color tr(0x48, 0x4e, 0x55); + color lb(static_cast(0x9dabb9)); + color tr(static_cast(0x484e55)); graph.frame_rectangle(r.pare_off(1), lb, tr, tr, lb); } private: @@ -168,7 +168,7 @@ namespace nana const unsigned half = (height - 2) / 2; int left = x + 1; int top = y + 1; - nana::color clr_top(0xea, 0xea, 0xea), clr_bottom(0xdc, 0xdc, 0xdc); + nana::color clr_top(static_cast(0xEAEAEA)), clr_bottom(static_cast(0xDCDCDC)); switch(state) { case mouse_action::over: @@ -192,7 +192,7 @@ namespace nana int bottom = y + height - 1; int right = x + width - 1; - graph.set_color(color(0x6e, 0x8d, 0x9f)); + graph.set_color(static_cast(0x6E8D9F)); graph.line(point{ x, y }, point{right, y}); graph.line(point{ x, y + 1 }, point{ x, bottom }); ++x; @@ -207,8 +207,6 @@ namespace nana ui_element ui_el_; struct style_tag { - //nana::color_t bgcolor; - //nana::color_t fgcolor; color bgcolor; color fgcolor; }style_; @@ -217,8 +215,8 @@ namespace nana class tree_wrapper { public: - typedef widgets::detail::tree_cont container; - typedef container::node_type * node_handle; + using container = widgets::detail::tree_cont; + using node_handle = container::node_type*; tree_wrapper() :splitstr_(STR("\\")), cur_(nullptr) @@ -495,12 +493,11 @@ namespace nana bool erase_locate() { ui_el_.index = npos; - if(ui_el_.what != ui_el_.none) - { - ui_el_.what = ui_el_.none; - return true; - } - return false; + if(ui_el_.what == ui_el_.none) + return false; + + ui_el_.what = ui_el_.none; + return true; } ui_element locate() const @@ -528,13 +525,8 @@ namespace nana if(style_.mode != mode::floatlist) { style_.state = mouse_action::normal; - switch(ui_el_.what) - { - case ui_element::item_name: + if (ui_element::item_name == ui_el_.what) _m_selected(treebase_.tail(ui_el_.index)); - break; - default: break; - } } } @@ -882,9 +874,8 @@ namespace nana void trigger::_m_event_agent_ready() const { - auto & evt = scheme_->evt_holder(); auto evt_agent = event_agent_.get(); - evt.selected = [evt_agent](::nana::any& val){ + scheme_->evt_holder().selected = [evt_agent](::nana::any& val){ evt_agent->selected(val); }; } diff --git a/source/gui/widgets/checkbox.cpp b/source/gui/widgets/checkbox.cpp index 8993a8b7..49626834 100644 --- a/source/gui/widgets/checkbox.cpp +++ b/source/gui/widgets/checkbox.cpp @@ -48,29 +48,35 @@ namespace checkbox void drawer::refresh(graph_reference graph) { - _m_draw(graph); + _m_draw_background(graph); + _m_draw_title(graph); + _m_draw_checkbox(graph, graph.text_extent_size(STR("jN"), 2).height + 2); } void drawer::mouse_down(graph_reference graph, const arg_mouse&) { - _m_draw(graph); + refresh(graph); + API::lazy_refresh(); } void drawer::mouse_up(graph_reference graph, const arg_mouse&) { if(impl_->react) impl_->crook.reverse(); - _m_draw(graph); + refresh(graph); + API::lazy_refresh(); } void drawer::mouse_enter(graph_reference graph, const arg_mouse&) { - _m_draw(graph); + refresh(graph); + API::lazy_refresh(); } void drawer::mouse_leave(graph_reference graph, const arg_mouse&) { - _m_draw(graph); + refresh(graph); + API::lazy_refresh(); } drawer::implement * drawer::impl() const @@ -78,14 +84,6 @@ namespace checkbox return impl_; } - void drawer::_m_draw(graph_reference graph) - { - _m_draw_background(graph); - _m_draw_title(graph); - _m_draw_checkbox(graph, graph.text_extent_size(STR("jN"), 2).height + 2); - API::lazy_refresh(); - } - void drawer::_m_draw_background(graph_reference graph) { if(bground_mode::basic != API::effects_bground_mode(*widget_)) @@ -177,6 +175,7 @@ namespace checkbox void checkbox::radio(bool is_radio) { get_drawer_trigger().impl()->crook.radio(is_radio); + API::refresh_window(handle()); } void checkbox::transparent(bool enabled) @@ -185,6 +184,7 @@ namespace checkbox API::effects_bground(*this, effects::bground_transparent(0), 0.0); else API::effects_bground_remove(*this); + API::refresh_window(handle()); } bool checkbox::transparent() const @@ -196,10 +196,12 @@ namespace checkbox //class radio_group radio_group::~radio_group() { - for(auto & i : ui_container_) + for(auto & e : ui_container_) { - API::umake_event(i.eh_checked); - API::umake_event(i.eh_destroy); + e.uiobj->radio(false); + e.uiobj->react(true); + API::umake_event(e.eh_checked); + API::umake_event(e.eh_destroy); } } @@ -231,7 +233,7 @@ namespace checkbox return ui_container_.size(); } - void radio_group::_m_checked(const arg_mouse& arg) + void radio_group::_m_checked(const arg_click& arg) { for (auto & i : ui_container_) i.uiobj->check(arg.window_handle == i.uiobj->handle()); diff --git a/source/gui/widgets/combox.cpp b/source/gui/widgets/combox.cpp index b1d401f5..c6f3a32f 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) @@ -199,20 +201,16 @@ namespace nana if (new_where == state_.pointer_where) return false; - + state_.pointer_where = new_where; return true; } - void set_mouse_over(bool mo) + void set_button_state(element_state state, bool reset_where) { - state_.button_state = (mo ? element_state::hovered : element_state::normal); - state_.pointer_where = parts::none; - } - - void set_mouse_press(bool mp) - { - state_.button_state = (mp ? element_state::pressed : element_state::hovered); + state_.button_state = state; + if (reset_where) + state_.pointer_where = parts::none; } void set_focused(bool f) @@ -263,7 +261,7 @@ namespace nana state_.lister->move_items(upwards, circle); return; } - + auto pos = module_.index; if (upwards) { @@ -414,7 +412,7 @@ namespace nana { if (image_pixels_ == px) return false; - + image_pixels_ = px; return true; } @@ -521,7 +519,7 @@ namespace nana } nana::point pos((image_pixels_ - imgsz.width) / 2 + 2, (vpix - imgsz.height) / 2 + 2); - img.stretch(img.size(), *graph_, nana::rectangle(pos, imgsz)); + img.stretch(::nana::rectangle{ img.size() }, *graph_, nana::rectangle(pos, imgsz)); } private: std::vector> items_; @@ -598,7 +596,7 @@ namespace nana void trigger::mouse_enter(graph_reference, const arg_mouse&) { - drawer_->set_mouse_over(true); + drawer_->set_button_state(element_state::hovered, true); if(drawer_->widget_ptr()->enabled()) { drawer_->draw(); @@ -608,7 +606,7 @@ namespace nana void trigger::mouse_leave(graph_reference, const arg_mouse&) { - drawer_->set_mouse_over(false); + drawer_->set_button_state(element_state::normal, true); drawer_->editor()->mouse_enter(false); if(drawer_->widget_ptr()->enabled()) { @@ -619,11 +617,12 @@ namespace nana void trigger::mouse_down(graph_reference graph, const arg_mouse& arg) { - drawer_->set_mouse_press(true); + //drawer_->set_mouse_press(true); + drawer_->set_button_state(element_state::pressed, false); if(drawer_->widget_ptr()->enabled()) { auto * editor = drawer_->editor(); - if(false == editor->mouse_down(arg.left_button, arg.pos)) + if(false == editor->mouse_down(arg.button, arg.pos)) drawer_->open_lister_if_push_button_positioned(); drawer_->draw(); @@ -638,8 +637,8 @@ namespace nana { if (drawer_->widget_ptr()->enabled() && !drawer_->has_lister()) { - drawer_->editor()->mouse_up(arg.left_button, arg.pos); - drawer_->set_mouse_press(false); + drawer_->editor()->mouse_up(arg.button, arg.pos); + drawer_->set_button_state(element_state::hovered, false); drawer_->draw(); API::lazy_refresh(); } @@ -685,7 +684,7 @@ namespace nana { case keyboard::os_arrow_left: case keyboard::os_arrow_right: - drawer_->editor()->respond_key(arg.key); + drawer_->editor()->respond_key(arg); drawer_->editor()->reset_caret(); break; case keyboard::os_arrow_up: @@ -714,14 +713,14 @@ namespace nana } } if (call_other_keys) - drawer_->editor()->respond_key(arg.key); + drawer_->editor()->respond_key(arg); API::lazy_refresh(); } void trigger::key_char(graph_reference graph, const arg_keyboard& arg) { - if (drawer_->editor()->respond_char(arg.key)) + if (drawer_->editor()->respond_char(arg)) API::lazy_refresh(); } //end class trigger @@ -989,7 +988,7 @@ namespace nana API::refresh_window(*this); } - nana::string combox::_m_caption() const + nana::string combox::_m_caption() const throw() { internal_scope_guard lock; auto editor = _m_impl().editor(); diff --git a/source/gui/widgets/date_chooser.cpp b/source/gui/widgets/date_chooser.cpp index 3f1818ca..7da5474c 100644 --- a/source/gui/widgets/date_chooser.cpp +++ b/source/gui/widgets/date_chooser.cpp @@ -80,37 +80,6 @@ namespace nana return where::none; } - void trigger::_m_draw(graph_reference graph) - { - const unsigned width = graph.width() - 2; - - graph.rectangle(false, {0xb0, 0xb0, 0xb0}); - graph.rectangle({ 1, 1, width, static_cast(topbar_height) }, true, colors::white); - - _m_draw_topbar(graph); - - if(graph.height() > 2 + topbar_height) - { - nana::point refpos(1, static_cast(topbar_height) + 1); - - nana::paint::graphics gbuf({ width, graph.height() - 2 - topbar_height }); - gbuf.rectangle(true, {0xf0, 0xf0, 0xf0}); - - switch(page_) - { - case page::date: - _m_draw_days(refpos, gbuf); - break; - case page::month: - _m_draw_months(refpos, gbuf); - break; - default: break; - } - - graph.bitblt(refpos.x, refpos.y, gbuf); - } - } - void trigger::_m_draw_topbar(graph_reference graph) { ::nana::color arrow_bgcolor; @@ -414,7 +383,7 @@ namespace nana r.y = static_cast(newbuf.height() - r.height) / 2; newbuf.stretch(nzbuf, r); - nzbuf.blend(nzbuf.size(), dzbuf, nana::point(), fade * (count - i)); + nzbuf.blend(::nana::rectangle{ nzbuf.size() }, dzbuf, nana::point(), fade * (count - i)); graph.bitblt(refpos.x, refpos.y, dzbuf); API::update_window(*widget_); @@ -442,7 +411,7 @@ namespace nana nzbuf.rectangle(true, colors::white); newbuf.stretch(nzbuf, r); - nzbuf.blend(nzbuf.size(), dzbuf, nana::point(), fade * (count - i)); + nzbuf.blend(::nana::rectangle{ nzbuf.size() }, dzbuf, nana::point(), fade * (count - i)); graph.bitblt(refpos.x, refpos.y, dzbuf); API::update_window(*widget_); @@ -455,7 +424,33 @@ namespace nana void trigger::refresh(graph_reference graph) { - _m_draw(graph); + const unsigned width = graph.width() - 2; + + graph.rectangle(false, { 0xb0, 0xb0, 0xb0 }); + graph.rectangle({ 1, 1, width, static_cast(topbar_height) }, true, colors::white); + + _m_draw_topbar(graph); + + if (graph.height() > 2 + topbar_height) + { + nana::point refpos(1, static_cast(topbar_height)+1); + + nana::paint::graphics gbuf({ width, graph.height() - 2 - topbar_height }); + gbuf.rectangle(true, { 0xf0, 0xf0, 0xf0 }); + + switch (page_) + { + case page::date: + _m_draw_days(refpos, gbuf); + break; + case page::month: + _m_draw_months(refpos, gbuf); + break; + default: break; + } + + graph.bitblt(refpos.x, refpos.y, gbuf); + } } void trigger::attached(widget_reference widget, graph_reference) @@ -468,7 +463,7 @@ namespace nana where pos = _m_pos_where(graph, arg.pos); if(pos == pos_ && pos_ != where::textarea) return; pos_ = pos; - _m_draw(graph); + refresh(graph); API::lazy_refresh(); } @@ -476,7 +471,7 @@ namespace nana { if(where::none == pos_) return; pos_ = where::none; - _m_draw(graph); + refresh(graph); API::lazy_refresh(); } @@ -595,7 +590,7 @@ namespace nana nana::paint::graphics dirtybuf({ r.width, r.height }); dirtybuf.bitblt(r, graph, refpos); - _m_draw(graph); + refresh(graph); nana::paint::graphics gbuf({ r.width, r.height }); gbuf.bitblt(r, graph, refpos); @@ -603,7 +598,7 @@ namespace nana _m_perf_transform(tfid, graph, dirtybuf, gbuf, refpos); } else - _m_draw(graph); + refresh(graph); API::lazy_refresh(); } diff --git a/source/gui/widgets/float_listbox.cpp b/source/gui/widgets/float_listbox.cpp index 8af19e43..1af70c6c 100644 --- a/source/gui/widgets/float_listbox.cpp +++ b/source/gui/widgets/float_listbox.cpp @@ -34,7 +34,7 @@ namespace nana { if (state == StateHighlighted) { - ::nana::color clr{ 0xaf, 0xc7, 0xe3 }; + ::nana::color clr(static_cast(0xafc7e3)); graph.rectangle(r, false, clr); auto right = r.right() - 1; @@ -99,7 +99,7 @@ namespace nana nana::point to_pos(x, r.y + 2); to_pos.x += (image_pixels_ - imgsz.width) / 2; to_pos.y += (vpix - imgsz.height) / 2; - item->image().stretch(item->image().size(), graph, nana::rectangle(to_pos, imgsz)); + item->image().stretch(::nana::rectangle{ item->image().size() }, graph, nana::rectangle(to_pos, imgsz)); } x += (image_pixels_ + 2); } @@ -276,7 +276,7 @@ namespace nana } } - bool right_area(graph_reference graph, int x, int y) const + static bool right_area(graph_reference graph, int x, int y) { return ((1 < x && 1 < y) && x < static_cast(graph.width()) - 2 && @@ -325,7 +325,7 @@ namespace nana _m_open_scrollbar(*widget_, pages); } else - graph_->string({ 4, 4 }, STR("Empty Listbox, No Module!"), {0x80, 0x80, 0x80}); + graph_->string({ 4, 4 }, STR("Empty Listbox, No Module!"), static_cast(0x808080)); //Draw border graph_->rectangle(false, colors::black); @@ -361,7 +361,7 @@ namespace nana auto fn = [this](const arg_mouse& arg) { - if (arg.left_button && (scrollbar_.value() != state_.offset_y)) + if (arg.is_left_button() && (scrollbar_.value() != state_.offset_y)) { state_.offset_y = static_cast(scrollbar_.value()); draw(); @@ -449,12 +449,16 @@ namespace nana void trigger::mouse_up(graph_reference graph, const arg_mouse& arg) { - if(drawer_->right_area(graph, arg.pos.x, arg.pos.y)) + bool close_wdg = false; + if (drawer_->right_area(graph, arg.pos.x, arg.pos.y)) { drawer_->set_result(); - drawer_->widget_ptr()->close(); + close_wdg = true; } - else if(false == drawer_->ignore_emitting_mouseup()) + else + close_wdg = (false == drawer_->ignore_emitting_mouseup()); + + if (close_wdg) drawer_->widget_ptr()->close(); } //end class trigger diff --git a/source/gui/widgets/form.cpp b/source/gui/widgets/form.cpp index 0c8cfc43..eab587ad 100644 --- a/source/gui/widgets/form.cpp +++ b/source/gui/widgets/form.cpp @@ -18,9 +18,10 @@ namespace nana namespace form { //class trigger - void trigger::attached(widget_reference widget, graph_reference graph) + void trigger::attached(widget_reference wdg, graph_reference graph) { - wd_ = &widget; + wd_ = &wdg; + API::ignore_mouse_focus(wdg, true); } void trigger::refresh(graph_reference graph) diff --git a/source/gui/widgets/group.cpp b/source/gui/widgets/group.cpp new file mode 100644 index 00000000..37fb20e1 --- /dev/null +++ b/source/gui/widgets/group.cpp @@ -0,0 +1,249 @@ +/** + * A group widget implementation + * Nana C++ Library(http://www.nanaro.org) + * Copyright(C) 2015 Jinhao(cnjinhao@hotmail.com) + * + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * @file: nana/gui/widgets/group.cpp + * + * @Author: Stefan Pfeifer(st-321), Ariel Vina-Rodriguez (qPCR4vir) + * + * @brief group is a widget used to visually group and layout other widgets. + */ + + +#include +#include +#include +#include + +#define _THROW_IF_EMPTY()\ + if(empty()) \ + throw std::logic_error("the group is invalid"); + +namespace nana{ + + static const char* field_title = "__nana_group_title__"; + static const char* field_options = "__nana_group_options__"; + + struct group::implement + { + label caption; + place place_content; + unsigned gap{2}; + std::string usr_div_str; + + std::vector> options; + radio_group * radio_logic{nullptr}; + + implement() = default; + + implement(window grp_panel, ::nana::string titel, bool vsb, unsigned gap=2) + : caption (grp_panel, std::move(titel), vsb), + place_content{grp_panel}, + gap{gap} + { + } + + void create(window pnl) + { + caption.create(pnl); + caption.caption(STR("")); + place_content.bind(pnl); + + if (!radio_logic) + radio_logic = new radio_group; + } + + void update_div() + { + ::nana::size sz = caption.measure(1000); + + std::stringstream ss; + ss << "vert margin=[0," << gap << "," << gap + 5 << "," << gap << "]" + << " <" << field_title << " weight=" << sz.width + 1 << "> >" + << "<"; + + if (!usr_div_str.empty()) + ss << "<" << usr_div_str << ">>"; + else + ss << ">"; + + place_content.div(ss.str().c_str()); + + if (options.empty()) + place_content.field_display(field_options, false); + } + }; + + group::group() + : impl_(new implement) + { + } + + group::group(window parent, const rectangle& r, bool vsb) + : group() + { + create(parent, r, vsb); + } + + group::group(window parent, ::nana::string titel, bool formatted, unsigned gap, const rectangle& r, bool vsb) + : panel(parent, r, vsb), + impl_(new implement(*this, std::move(titel), vsb, gap)) + { + impl_->caption.format(formatted); + _m_init(); + } + + group::~group() + { + delete impl_->radio_logic; + } + + group& group::add_option(::nana::string text) + { + _THROW_IF_EMPTY() + + impl_->options.emplace_back(new checkbox(handle())); + auto & opt = impl_->options.back(); + opt->transparent(true); + opt->caption(std::move(text)); + impl_->place_content[field_options] << *opt; + impl_->place_content.field_display(field_options, true); + impl_->place_content.collocate(); + + if (impl_->radio_logic) + impl_->radio_logic->add(*opt); + + return *this; + } + + group& group::radio_mode(bool enable) + { + _THROW_IF_EMPTY() + + if (enable) + { + //Create radio_group if it is null + if (!impl_->radio_logic) + impl_->radio_logic = new ::nana::radio_group; + + //add all options into the radio_group + for (auto & opt : impl_->options) + impl_->radio_logic->add(*opt); + } + else + { + delete impl_->radio_logic; + impl_->radio_logic = nullptr; + } + return *this; + } + + std::size_t group::option() const + { + _THROW_IF_EMPTY(); + + if (impl_->radio_logic) + return impl_->radio_logic->checked(); + + throw std::logic_error("the radio_mode of the group is disabled"); + } + + bool group::option_checked(std::size_t pos) const + { + _THROW_IF_EMPTY(); + return impl_->options.at(pos)->checked(); + } + + group& group::enable_format_caption(bool format) + { + impl_->caption.format(format); + return *this; + } + + group& group::collocate() throw () + { + impl_->place_content.collocate(); + return *this; + } + + group& group::div(const char* div_str) throw () + { + if (div_str) + impl_->usr_div_str = div_str; + else + impl_->usr_div_str.clear(); + + impl_->update_div(); + return *this; + } + + group::field_reference group::operator[](const char* field) + { + return impl_->place_content.field(field); + } + + void group::_m_add_child(const char* field, widget* wdg) + { + impl_->place_content[field] << wdg->handle(); + } + + void group::_m_init() + { + this->div(nullptr); + + auto & outter = impl_->place_content; + + outter[field_title] << impl_->caption; + outter.collocate(); + + color pbg = API::bgcolor(this->parent()); + impl_->caption.bgcolor(pbg.blend(colors::black, 0.975)); + color bg = pbg.blend(colors::black, 0.950); + + bgcolor(bg); + + drawing dw(*this); + + ::nana::size sz = impl_->caption.measure(1000); + + // This drawing function is owner by the onwer of dw (the outer panel of the group widget), not by dw !! + dw.draw([this, sz, bg, pbg](paint::graphics& graph) + { + auto gap_px = impl_->gap - 1; + + graph.rectangle(true, pbg); + graph.round_rectangle(rectangle(point(gap_px, sz.height / 2), + nana::size(graph.width() - 2 * gap_px, graph.height() - sz.height / 2 - gap_px) + ), + 3, 3, colors::gray_border, true, bg); + }); + } + + void group::_m_complete_creation() + { + panel::_m_complete_creation(); + + impl_->create(handle()); + + + _m_init(); + } + + ::nana::string group::_m_caption() const throw() + { + return impl_->caption.caption(); + } + + void group::_m_caption(::nana::string&& str) + { + impl_->caption.caption(std::move(str)); + impl_->update_div(); + impl_->place_content.collocate(); + } +}//end namespace nana + diff --git a/source/gui/widgets/label.cpp b/source/gui/widgets/label.cpp index 66e76027..6639bc53 100644 --- a/source/gui/widgets/label.cpp +++ b/source/gui/widgets/label.cpp @@ -628,6 +628,8 @@ namespace nana nana::string target; //It indicates which target is tracing. nana::string url; + window for_associated_wd{ nullptr }; + void add_listener(std::function&& fn) { listener_.emplace_back(std::move(fn)); @@ -730,7 +732,7 @@ namespace nana } } - void trigger::click(graph_reference, const arg_mouse&) + void trigger::click(graph_reference, const arg_click&) { //make a copy, because the listener may popup a window, and then //user moves the mouse. it will reset the url when the mouse is moving out from the element. @@ -740,6 +742,8 @@ namespace nana impl_->call_listener(command::click, impl_->target); system::open_url(url); + + API::focus_window(impl_->for_associated_wd); } void trigger::refresh(graph_reference graph) @@ -775,13 +779,6 @@ namespace nana caption(text); } - label::label(window wd, const nana::char_t* text, bool visible) - { - create(wd, rectangle(), visible); - bgcolor(API::bgcolor(wd)); - caption(text); - } - label::label(window wd, const rectangle& r, bool visible) { create(wd, r, visible); @@ -797,7 +794,7 @@ namespace nana return *this; } - bool label::transparent() const + bool label::transparent() const throw() { return (bground_mode::basic == API::effects_bground_mode(*this)); } @@ -821,6 +818,12 @@ namespace nana return *this; } + label& label::click_for(window associated_window) throw() + { + get_drawer_trigger().impl()->for_associated_wd = associated_window; + return *this; + } + nana::size label::measure(unsigned limited) const { if(empty()) diff --git a/source/gui/widgets/listbox.cpp b/source/gui/widgets/listbox.cpp index 53ee6eff..8487bb59 100644 --- a/source/gui/widgets/listbox.cpp +++ b/source/gui/widgets/listbox.cpp @@ -13,6 +13,9 @@ #include #include +#include + +#include #include #include #include @@ -368,12 +371,11 @@ namespace nana bool visible(bool v) { - if(visible_ != v) - { - visible_ = v; - return true; - } - return false; + if (visible_ == v) + return false; + + visible_ = v; + return true; } bool sortable() const @@ -399,21 +401,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) { - if (pos >= cont_.size()) - return; - for(auto & m : cont_) - { if(m.index == pos) + { m.pixels = width; - } + return; + } } unsigned item_width(size_type pos) const @@ -630,23 +631,27 @@ namespace nana return *this; } - nana::string to_string(const export_options& exp_opt) const - { - nana::string item_str; - bool first{true}; - for( size_type idx{}; idx; @@ -659,6 +664,9 @@ namespace nana //A cat may have a key object to identify the category std::shared_ptr key_ptr; + std::deque>> factories; + std::deque> indicators; + category_t() = default; category_t(nana::string str) @@ -726,8 +734,6 @@ namespace nana if((sorted_index_ == npos) || (!resort_)) return; - - auto weak_ordering_comp = fetch_ordering_comparer(sorted_index_); if(weak_ordering_comp) { @@ -852,13 +858,6 @@ namespace nana return &list_.back(); } - /// just append a list of new cat. - void create_cat(const std::initializer_list& args) - { - for (auto & arg : args) - list_.emplace_back(arg); - } - /// will use the key to insert new cat before the first cat with compare less than the key, or at the end of the list of cat and return a ref to that new cat. ? category_t* create_cat(std::shared_ptr ptr) { @@ -934,6 +933,11 @@ namespace nana return npos ; } + category_t& at(std::size_t cat_pos) + { + return *(_m_at(cat_pos)); + } + /// return a ref to the real item object at display!!! position pos using current sorting only if it is active, and at absolute position if no sorting is currently active. category_t::container::value_type& at(const index_pair& pos) { @@ -944,9 +948,15 @@ namespace nana return _m_at(pos.cat)->items.at(index); } + const category_t::container::value_type& at(const index_pair& pos) const { - return at(pos); + auto index = pos.item; + + if (sorted_index_ != npos) + index = absolute(pos); + + return _m_at(pos.cat)->items.at(index); } void clear(size_type cat) @@ -1063,17 +1073,10 @@ namespace nana std::vector& get_cells(category_t * cat, size_type pos) const { - if (!cat || pos >= cat->items.size()) - throw std::out_of_range("nana::listbox: bad item position"); + if (!cat) + throw std::out_of_range("nana::listbox: category is null"); - return cat->items[pos].cells; - } - - nana::string text(category_t* cat, size_type pos, size_type col) const - { - if (pos < cat->items.size() && (col < cat->items[pos].cells.size())) - return cat->items[pos].cells[col].text; - return{}; + return cat->items.at(pos).cells; } void text(category_t* cat, size_type pos, size_type col, cell&& cl, size_type columns) @@ -1592,31 +1595,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) { @@ -1644,9 +1623,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()); @@ -1656,32 +1648,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; } @@ -1689,7 +1681,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); @@ -1726,74 +1718,45 @@ namespace nana { return {pos.cat, relative( pos )}; } - - /// all arg are relative to display order, or all are absolute, but not mixed + + /// all arg are relative to display order, or all are absolute, but not mixed bool forward(index_pair from, size_type offs, index_pair& item) const { - if(!good_item(from, from)) + if (!good_item(from, from)) return false; - if(offs == 0) + auto cat = _m_at(from.cat); + auto cat_end = list_.end(); + + auto items_left = (cat->expand ? cat->items.size() : 0); + + if (from.is_category()) + items_left += 1; //add 1 category bar + else if (items_left >= from.item) + items_left -= from.item; + else + return false; //invalid argument + + while (offs) { - item = from; - return true; - } - - if(list_.size() <= from.cat) return false; - - if(from.is_category()) - { - // this is a category, so... - // and offs is not 0, this category would not be candidated. - // the algorithm above to calc the offset item is always starting with a item. - --offs; - from.item = 0; - } - - auto icat = _m_at(from.cat); // an iterator to category from.cat - - if(icat->expand) - { - std::size_t item_left_in_this_cat = icat->items.size() -1- from.item; - if(offs <= item_left_in_this_cat ) - { - item = from; - item.item += offs; // use absolute to know the real item - return true; // allways return here when we have only one cat. - } - else - { - offs -= item_left_in_this_cat ; - item = from; - item.item += item_left_in_this_cat ; - } - } - - ++from.cat; - ++icat; - for(; icat != list_.end(); ++icat, ++from.cat) - { - item.cat = from.cat; - item.item = npos; - - if(offs-- == 0) + if (items_left > offs) { + item.cat = from.cat; + item.item = (npos == from.item ? offs - 1 : from.item + offs); return true; } - if(icat->expand) - { - if(offs < icat->items.size()) - { - //item.cat = from.cat; - item.item = offs; - return true; - } - else - offs -= icat->items.size(); - } + offs -= items_left; + if (++cat == cat_end) + return false; + + ++from.cat; + from.item = npos; + items_left = (cat->expand ? cat->items.size() + 1 : 1); } - return false; + + item = from; + return true; } /// all arg are relative to display order, or all are absolute, but not mixed @@ -1843,7 +1806,7 @@ namespace nana container::iterator _m_at(size_type index) { if(index >= list_.size()) - throw std::out_of_range("Nana.GUI.Listbox: invalid category index"); + throw std::out_of_range("nana::listbox: invalid category index"); auto i = list_.begin(); std::advance(i, index); @@ -1853,7 +1816,7 @@ namespace nana container::const_iterator _m_at(size_type index) const { if(index >= list_.size()) - throw std::out_of_range("Nana.GUI.Listbox: invalid category index"); + throw std::out_of_range("nana::listbox: invalid category index"); auto i = list_.cbegin(); std::advance(i, index); @@ -1877,6 +1840,7 @@ namespace nana bool single_check_category_limited_{ false }; };//end class es_lister + //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. @@ -1894,7 +1858,8 @@ namespace nana unsigned item_size{24}; unsigned text_height{0}; unsigned suspension_width{0}; - ::nana::listbox::export_options def_exp_options ; + + ::nana::listbox::export_options def_exp_options; ::nana::listbox::export_options& def_export_options() { @@ -1921,6 +1886,19 @@ namespace nana nana::scroll h; }scroll; + + struct inline_pane + { + ::nana::panel pane_bottom; //pane for pane_widget + ::nana::panel pane_widget; //pane for placing user-define widget + std::unique_ptr inline_ptr; + inline_indicator * indicator; + index_pair item_pos; //The item index of the inline widget + std::size_t column_pos; + }; + + std::map>> inline_table, inline_buffered_table; + essence_t() { scroll.offset_x = 0; @@ -2105,11 +2083,11 @@ namespace nana { scroll.h.create(wd, r); API::take_active(scroll.h.handle(), false, wd); - scroll.h.events().mouse_move.connect_unignorable([this](const nana::arg_mouse& arg){ - _m_answer_scroll(arg); - }); - scroll.h.events().mouse_up.connect_unignorable([this](const nana::arg_mouse& arg){ - _m_answer_scroll(arg); + + scroll.h.events().value_changed.connect_unignorable([this](const ::nana::arg_scroll& arg) + { + scroll.offset_x = static_cast(scroll.h.value()); + API::refresh_window(this->lister.wd_ptr()->handle()); }); } else @@ -2124,13 +2102,20 @@ namespace nana if(scroll.v.empty()) { scroll.v.create(wd, r); - API::take_active(scroll.v.handle(), false, wd); // install value_changed() not mouse_move ???? + API::take_active(scroll.v.handle(), false, wd); - scroll.v.events().value_changed([this](const ::nana::arg_scroll& arg) + scroll.v.events().value_changed([this](const ::nana::arg_scroll& arg) { - _m_answer_scroll_value(arg); - }); + index_pair item; + if (!lister.forward(item, scroll.v.value(), item)) return; + if (item == scroll.offset_y_dpl) + return; + + set_scroll_y_dpl(item); + + API::refresh_window(this->lister.wd_ptr()->handle()); + }); } else scroll.v.move(r); @@ -2139,7 +2124,7 @@ namespace nana else if(!scroll.v.empty()) { scroll.v.close(); - set_scroll_y_dpl({0,0}); // scroll.offset_y.set_both(0); + set_scroll_y_dpl({0,0}); nana::rectangle r; if(rect_header(r)) @@ -2195,7 +2180,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) { @@ -2309,168 +2294,236 @@ namespace nana for(const auto& hd : header.cont()) { if(false == hd.visible) continue; - x += hd.pixels; + x += static_cast(hd.pixels); if(x > 0) seqs.push_back(hd.index); if(x >= static_cast(lister_w)) break; } } + + 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; + auto i = inline_buffered_table.find(factory); + if (i != inline_buffered_table.end()) + { + auto & panes = i->second; + if (!panes.empty()) + { + pane_ptr = std::move(panes.front()); + panes.pop_front(); + } + } + + if (!pane_ptr) + { + pane_ptr.reset(new inline_pane); + pane_ptr->indicator = indicator; + pane_ptr->pane_bottom.create(this->lister.wd_ptr()->handle()); + pane_ptr->pane_widget.create(pane_ptr->pane_bottom); + pane_ptr->inline_ptr = factory->create(); + pane_ptr->inline_ptr->create(pane_ptr->pane_widget); + } + + auto ptr = pane_ptr.get(); + inline_table[factory].emplace_back(std::move(pane_ptr)); + return ptr; + } + }; + + class inline_indicator + : public ::nana::detail::inline_widget_indicator + { + public: + using parts = essence_t::parts; + + inline_indicator(essence_t* ess, std::size_t column_pos) + : ess_{ ess }, column_pos_{column_pos} + { + } + + ::nana::widget& host() const override + { + return *ess_->lister.wd_ptr(); + } + + void modify(index_type pos, const value_type& value) const override + { + auto & cells = ess_->lister.at(pos).cells; + if (cells.size() <= column_pos_) + cells.resize(column_pos_ + 1); + + cells[column_pos_].text = value; + ess_->update(); + } + + void selected(index_type pos) override + { + if (ess_->lister.at(pos).flags.selected) + return; + ess_->lister.select_for_all(false); + cat_proxy(ess_, pos.cat).at(pos.item).select(true); + } + + void hovered(index_type pos) override + { + auto offset = ess_->lister.distance(ess_->scroll.offset_y_dpl, pos); + + if (ess_->pointer_where.first != parts::lister || ess_->pointer_where.second != offset) + { + ess_->pointer_where.first = parts::lister; + ess_->pointer_where.second = offset; + ess_->update(); + } + } private: - void _m_answer_scroll(const arg_mouse& arg) + essence_t * const ess_; + const std::size_t column_pos_; + }; + + void es_lister::scroll_refresh() + { + ess_->scroll_y_dpl_refresh(); + } + + void es_lister::move_select(bool upwards, bool unselect_previous, bool trace_selected) + { + auto next_selected_dpl = relative_pair ( last_selected_abs); // last_selected_dpl; // ?? + if (next_selected_dpl.empty()) // has no cat ? (cat == npos) => beging from first cat { - if(arg.evt_code == event_code::mouse_move && arg.left_button == false) return; - - bool update = false; - if(arg.window_handle == scroll.v.handle()) + bool good = false; + for(size_type i = 0, size = list_.size(); i < size; ++i) // run all cat { - index_pair item; - if(lister.forward(item, scroll.v.value(), item)) + if(size_item(i)) { - if (item != scroll.offset_y_dpl) - { - set_scroll_y_dpl ( item ); - update = true; - } - } - } - else if(arg.window_handle == scroll.h.handle()) - { - if(scroll.offset_x != static_cast(scroll.h.value())) - { - scroll.offset_x = static_cast(scroll.h.value()); - update = true; - } - } - - if(update) - API::refresh_window(lister.wd_ptr()->handle()); - } - void _m_answer_scroll_value(const ::nana::arg_scroll& arg) - { - index_pair item; - if( !lister.forward(item, scroll.v.value(), item)) return; - - if (item == scroll.offset_y_dpl) - return; - - set_scroll_y_dpl ( item ); - - API::refresh_window(lister.wd_ptr()->handle()); - } - -}; - - void es_lister::scroll_refresh() - { - ess_->scroll_y_dpl_refresh(); - - } - void es_lister::move_select(bool upwards, bool unselect_previous, bool trace_selected) - { - auto next_selected_dpl = relative_pair ( last_selected_abs); // last_selected_dpl; // ?? - if (next_selected_dpl.empty()) // has no cat ? (cat == npos) => beging from first cat - { - bool good = false; - for(size_type i = 0, size = list_.size(); i < size; ++i) // run all cat - { - if(size_item(i)) - { - //The first category which contains at least one item. - next_selected_dpl.cat = i; - next_selected_dpl.item = 0; - good = true; - break; - } - } - if(! good ) return; // items in listbox : nothing to select (and an empty but visible cat?) - } - - //start moving - while(true) - { - if(upwards == false) - { - if(good(next_selected_dpl.cat)) - { - if (size_item(next_selected_dpl.cat) > next_selected_dpl.item + 1) - { - ++next_selected_dpl.item; - } - else - { - next_selected_dpl.item = 0; - if (size_categ() > next_selected_dpl.cat + 1) - ++next_selected_dpl.cat; - else - next_selected_dpl.cat = 0; - } - } - else - next_selected_dpl.set_both(0); - } - else - { - if (0 == next_selected_dpl.item) - { - //there is an item at least definitely, because the start pos is an available item. - do - { - if (0 == next_selected_dpl.cat) - next_selected_dpl.cat = size_categ() - 1; - else - --next_selected_dpl.cat; - - }while (0 == size_item(next_selected_dpl.cat)); - - next_selected_dpl.item = size_item(next_selected_dpl.cat) - 1; - } - else - --next_selected_dpl.item; - } - - if (good(next_selected_dpl.cat)) - { - expand(next_selected_dpl.cat, true); // revise expand - - if (good(next_selected_dpl)) - { - if (unselect_previous && !single_selection_ ) - select_for_all(false); - - /// 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::from_display(ess_, next_selected_dpl).select(true); - - if (trace_selected) - ess_->trace_item_dpl(next_selected_dpl); - } + //The first category which contains at least one item. + next_selected_dpl.cat = i; + next_selected_dpl.item = 0; + good = true; break; } - else break; } + if(! good ) return; // items in listbox : nothing to select (and an empty but visible cat?) } - nana::string es_lister::to_string(const export_options& exp_opt) const + //start moving + while(true) { - nana::string list_str; - bool first{true}; - for(auto & cat: cat_container()) + if(upwards == false) { - if(first) - first=false; - else - list_str += (cat.text + exp_opt.endl); - - for (auto i : cat.sorted) + if(good(next_selected_dpl.cat)) { - auto& it= cat.items[i] ; - if(it.flags.selected || !exp_opt.only_selected_items) - list_str += (it.to_string(exp_opt) + exp_opt.endl); + if (size_item(next_selected_dpl.cat) > next_selected_dpl.item + 1) + { + ++next_selected_dpl.item; + } + else + { + next_selected_dpl.item = 0; + if (size_categ() > next_selected_dpl.cat + 1) + ++next_selected_dpl.cat; + else + next_selected_dpl.cat = 0; + } } + else + next_selected_dpl.set_both(0); } - return list_str ; - } + else + { + if (0 == next_selected_dpl.item) + { + //there is an item at least definitely, because the start pos is an available item. + do + { + if (0 == next_selected_dpl.cat) + next_selected_dpl.cat = size_categ() - 1; + else + --next_selected_dpl.cat; + }while (0 == size_item(next_selected_dpl.cat)); + + next_selected_dpl.item = size_item(next_selected_dpl.cat) - 1; + } + else + --next_selected_dpl.item; + } + + if (good(next_selected_dpl.cat)) + { + expand(next_selected_dpl.cat, true); // revise expand + + if (good(next_selected_dpl)) + { + if (unselect_previous && !single_selection_ ) + select_for_all(false); + + /// 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::from_display(ess_, next_selected_dpl).select(true); + + if (trace_selected) + ess_->trace_item_dpl(next_selected_dpl); + } + break; + } + else break; + } + } + + nana::string es_lister::to_string(const export_options& exp_opt) const + { + nana::string list_str; + bool first{true}; + for(auto & cat: cat_container()) + { + if(first) + first=false; + else + list_str += (cat.text + exp_opt.endl); + + for (auto i : cat.sorted) + { + auto& it= cat.items[i] ; + if(it.flags.selected || !exp_opt.only_selected_items) + list_str += (it.to_string(exp_opt) + exp_opt.endl); + } + } + 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 { @@ -2500,7 +2553,7 @@ namespace nana { if(hd.visible) { - if((static_cast(hd.pixels) - 2 < x) && (x < static_cast(hd.pixels) + 3)) + if((static_cast(hd.pixels) < x + 2) && (x < static_cast(hd.pixels) + 3)) { item_spliter_ = hd.index; // original index return true; @@ -2550,9 +2603,8 @@ namespace nana essence_->header.item_width(item_spliter_, (new_w < static_cast(essence_->suspension_width + 20) ? essence_->suspension_width + 20 : new_w)); auto new_w = essence_->header.pixels(); if(new_w < (rect.width + essence_->scroll.offset_x)) - { essence_->scroll.offset_x = (new_w > rect.width ? new_w - rect.width : 0); - } + essence_->adjust_scroll_life(); return 2; } @@ -2610,7 +2662,6 @@ namespace nana void _m_draw(const Container& cont, const nana::rectangle& rect) { graph_reference graph = *(essence_->graph); - int txtop = (rect.height - essence_->text_height) / 2 + rect.y; auto txtcolor = essence_->lister.wd_ptr()->fgcolor(); @@ -2630,7 +2681,7 @@ namespace nana int next_x = x + static_cast(i.pixels); if(next_x > rect.x) { - _m_draw_item(graph, x, rect.y, height, txtop, txtcolor, i, (i.index == essence_->pointer_where.second ? state : item_state::normal)); + _m_draw_header_item(graph, x, rect.y, height, txtop, txtcolor, i, (i.index == essence_->pointer_where.second ? state : item_state::normal)); graph.line({ next_x - 1, rect.y }, { next_x - 1, bottom_y }, _m_border_color()); } @@ -2645,7 +2696,7 @@ namespace nana } template - void _m_draw_item(graph_reference graph, int x, int y, unsigned height, int txtop, const ::nana::color& fgcolor, const Item& item, item_state state) + void _m_draw_header_item(graph_reference graph, int x, int y, unsigned height, int txtop, const ::nana::color& fgcolor, const Item& item, item_state state) { essence_->scheme_ptr->header_bgcolor.get_color(); ::nana::color bgcolor; @@ -2677,10 +2728,10 @@ namespace nana ext_graph.typeface(essence_->graph->typeface()); int txtop = (essence_->header_size - essence_->text_height) / 2; - _m_draw_item(ext_graph, 0, 0, essence_->header_size, txtop, colors::white, item, item_state::floated); + _m_draw_header_item(ext_graph, 0, 0, essence_->header_size, txtop, colors::white, item, item_state::floated); int xpos = essence_->header.item_pos(item.index, nullptr) + pos.x - ref_xpos_; - ext_graph.blend(ext_graph.size(), *(essence_->graph), nana::point(xpos - essence_->scroll.offset_x + rect.x, rect.y), 0.5); + ext_graph.blend(rectangle{ ext_graph.size() }, *(essence_->graph), nana::point(xpos - essence_->scroll.offset_x + rect.x, rect.y), 0.5); } private: @@ -2707,8 +2758,6 @@ namespace nana void draw(const nana::rectangle& rect) const { - // essence_->scroll_y_dpl_refresh() ; // ???? - internal_scope_guard lock; size_type n = essence_->number_of_lister_items(true); @@ -2727,7 +2776,7 @@ namespace nana es_lister & lister = essence_->lister; //The Tracker indicates the item where mouse placed. index_pair tracker(npos, npos); - auto & ptr_where = essence_->pointer_where; + auto & ptr_where = essence_->pointer_where; //if where == lister || where == checker, 'second' indicates the offset to the relative display-order pos of the scroll offset_y which stands for the first item to be displayed in lister. if((ptr_where.first == parts::lister || ptr_where.first == parts::checker) && ptr_where.second != npos) @@ -2750,6 +2799,8 @@ namespace nana auto state = item_state::normal; + essence_->inline_buffered_table.swap(essence_->inline_table); + //Here we draw the root categ (0) or a first item if the first drawing is not a categ.(item!=npos)) if(idx.cat == 0 || !idx.is_category()) { @@ -2760,12 +2811,16 @@ namespace nana } std::size_t size = i_categ->items.size(); + index_pair item_index{ idx.cat, 0 }; for(std::size_t offs = essence_->scroll.offset_y_dpl.item; offs < size; ++offs, ++idx.item) { if(n-- == 0) break; state = (tracker == idx ? item_state::highlighted : item_state::normal); - _m_draw_item(i_categ->items[lister.absolute(index_pair(idx.cat, offs)) ], x, y, txtoff, header_w, rect, subitems, bgcolor,fgcolor, state); + item_index.item = offs; + item_index = lister.absolute_pair(item_index); + + _m_draw_item(*i_categ, item_index, x, y, txtoff, header_w, rect, subitems, bgcolor,fgcolor, state); y += essence_->item_size; } @@ -2787,17 +2842,23 @@ namespace nana continue; auto size = i_categ->items.size(); + index_pair item_pos{ idx.cat, 0 }; for(decltype(size) pos = 0; pos < size; ++pos) { if(n-- == 0) break; state = (idx == tracker ? item_state::highlighted : item_state::normal); - _m_draw_item(i_categ->items[ lister.absolute(index_pair(idx.cat, pos))], x, y, txtoff, header_w, rect, subitems, bgcolor, fgcolor, state); + item_pos.item = pos; + item_pos.item = lister.absolute(item_pos); + + _m_draw_item(*i_categ, item_pos, x, y, txtoff, header_w, rect, subitems, bgcolor, fgcolor, state); y += essence_->item_size; ++idx.item; } } + essence_->inline_buffered_table.clear(); + if (y < rect.y + static_cast(rect.height)) { essence_->graph->set_color(bgcolor); @@ -2846,8 +2907,12 @@ namespace nana } } - void _m_draw_item(const item_t& item, int x, int y, int txtoff, unsigned width, const nana::rectangle& r, const std::vector& seqs, nana::color bgcolor, nana::color fgcolor, item_state state) const + //Draws an item + //@param content_r the rectangle of list content + void _m_draw_item(const category_t& cat, const index_pair& item_pos, const int x, const int y, const int txtoff, unsigned width, const nana::rectangle& content_r, const std::vector& seqs, nana::color bgcolor, nana::color fgcolor, item_state state) const { + auto & item = cat.items[item_pos.item]; + if (item.flags.selected) // fetch the "def" colors bgcolor = essence_->scheme_ptr->item_selected; else if (!item.bgcolor.invisible()) @@ -2856,7 +2921,6 @@ namespace nana if(!item.fgcolor.invisible()) fgcolor = item.fgcolor; - auto graph = essence_->graph; if (item_state::highlighted == state) // and blend it if "highlighted" { if (item.flags.selected) @@ -2866,116 +2930,208 @@ namespace nana } unsigned show_w = width - essence_->scroll.offset_x; - if(show_w >= r.width) show_w = r.width; + if(show_w >= content_r.width) show_w = content_r.width; + auto graph = essence_->graph; //draw the background graph->set_color(bgcolor); - graph->rectangle(rectangle{ r.x, y, show_w, essence_->item_size }, true); + graph->rectangle(rectangle{ content_r.x, y, show_w, essence_->item_size }, true); int item_xpos = x; unsigned extreme_text = x; - bool first = true; - - for(size_type display_order{0}; display_order < seqs.size(); ++display_order) // get the cell (column) index in the order headers are displayed + for (size_type display_order{ 0 }; display_order < seqs.size(); ++display_order) // get the cell (column) index in the order headers are displayed { - auto index = seqs[display_order]; - const auto & header = essence_->header.column(index); // deduce the corresponding header which is in a kind of dislay order - auto it_bgcolor = bgcolor; + const auto column_pos = seqs[display_order]; + const auto & header = essence_->header.column(column_pos); // deduce the corresponding header which is in a kind of dislay order + auto it_bgcolor = bgcolor; - if ((item.cells.size() > index) && (header.pixels > 5)) // process only if the cell is visible + if (header.pixels > 5) { - auto cell_txtcolor = fgcolor; - auto & m_cell = item.cells[index]; - nana::size ts = graph->text_extent_size(m_cell.text); // precalcule text geometry + int content_pos = 5; - if (m_cell.custom_format && (!m_cell.custom_format->bgcolor.invisible())) // adapt to costum format if need + //Draw the image in the 1st column in display order + if (0 == display_order) { - it_bgcolor = m_cell.custom_format->bgcolor; - if (item.flags.selected) - it_bgcolor = it_bgcolor.blend( bgcolor , 0.5) ; - if (item_state::highlighted == state) - it_bgcolor = it_bgcolor.blend(::nana::color(0x99, 0xde, 0xfd), 0.8); - - graph->set_color(it_bgcolor); - graph->rectangle(rectangle{ item_xpos, y, header.pixels, essence_->item_size }, true); + if (essence_->checkable) + { + content_pos += 18; - cell_txtcolor = m_cell.custom_format->fgcolor; + element_state estate = element_state::normal; + if (essence_->pointer_where.first == parts::checker) + { + switch (state) + { + case item_state::highlighted: + estate = element_state::hovered; break; + case item_state::grabbed: + estate = element_state::pressed; break; + default: break; + } + } + + using state = facade::state; + crook_renderer_.check(item.flags.checked ? state::checked : state::unchecked); + crook_renderer_.draw(*graph, bgcolor, fgcolor, essence_->checkarea(item_xpos, y), estate); + } + + if (essence_->if_image) + { + //Draw the image in the 1st column in display order + if (item.img) + { + nana::rectangle img_r(item.img_show_size); + img_r.x = content_pos + item_xpos + static_cast(16 - item.img_show_size.width) / 2; + img_r.y = y + static_cast(essence_->item_size - item.img_show_size.height) / 2; + item.img.stretch(rectangle{ item.img.size() }, *graph, img_r); + } + content_pos += 18; + } } - int ext_w = 5; - if(first && essence_->checkable) // draw the checkbox if need, only before the first column (display_order=0 ?) - { - ext_w += 18; + bool draw_column = true; - element_state estate = element_state::normal; - if(essence_->pointer_where.first == parts::checker) + if (static_cast(content_pos) < header.pixels) + { + auto inline_wdg = _m_get_inline_pane(cat, column_pos); + if (inline_wdg) { - switch(state) + //Make sure the user-define inline widgets in right visible rectangle. + rectangle pane_r; + auto wdg_x = item_xpos + content_pos; + auto wdg_w = header.pixels - static_cast(content_pos); + + bool visible_state = true; + if (::nana::overlap(content_r, { wdg_x, y, wdg_w, essence_->item_size }, pane_r)) { - case item_state::highlighted: - estate = element_state::hovered; break; - case item_state::grabbed: - estate = element_state::pressed; break; - default: break; + ::nana::point pane_pos; + if (wdg_x < content_r.x) + pane_pos.x = wdg_x - content_r.x; + + if (y < content_r.y) + pane_pos.y = y - content_r.y; + + inline_wdg->pane_widget.move(pane_pos); + inline_wdg->pane_bottom.move(pane_r); + } + else + visible_state = false; + + ::nana::size sz{ wdg_w, essence_->item_size }; + inline_wdg->pane_widget.size(sz); + inline_wdg->inline_ptr->resize(sz); + inline_wdg->item_pos = item_pos; + inline_wdg->column_pos = column_pos; + inline_wdg->inline_ptr->activate(*inline_wdg->indicator, item_pos); + + draw_column = inline_wdg->inline_ptr->whether_to_draw(); + + //To reduce the memory usage, the cells may not be allocated + if (item.cells.size() > column_pos) + inline_wdg->inline_ptr->set(item.cells[column_pos].text); + else + inline_wdg->inline_ptr->set({}); + + API::show_window(inline_wdg->pane_bottom, visible_state); + } + } + + if (item.cells.size() > column_pos) // process only if the cell is visible + { + auto cell_txtcolor = fgcolor; + auto & m_cell = item.cells[column_pos]; + nana::size ts = graph->text_extent_size(m_cell.text); // precalcule text geometry + + if (m_cell.custom_format && (!m_cell.custom_format->bgcolor.invisible())) // adapt to costum format if need + { + it_bgcolor = m_cell.custom_format->bgcolor; + if (item.flags.selected) + it_bgcolor = it_bgcolor.blend(bgcolor, 0.5); + if (item_state::highlighted == state) + it_bgcolor = it_bgcolor.blend(static_cast(0x99defd), 0.8); + + graph->set_color(it_bgcolor); + + graph->rectangle(rectangle{ item_xpos, y, header.pixels, essence_->item_size }, true); + + cell_txtcolor = m_cell.custom_format->fgcolor; + } + + if (draw_column) + { + graph->set_text_color(cell_txtcolor); + graph->string(point{ item_xpos + content_pos, y + txtoff }, m_cell.text); // draw full text of the cell index (column) + + if (ts.width + static_cast(content_pos) > header.pixels) // it was an excess + { + //The text is painted over the next subitem // here beging the ... + int xpos = item_xpos + static_cast(header.pixels) - static_cast(essence_->suspension_width); + + graph->set_color(it_bgcolor); // litter rect with the item bg end ... + graph->rectangle(rectangle{ xpos, y + 2, essence_->suspension_width, essence_->item_size - 4 }, true); + graph->string(point{ xpos, y + 2 }, STR("...")); + + //Erase the part that over the next subitem. + if (display_order + 1 < seqs.size()) // this is not the last column + { + graph->set_color(bgcolor); // we need to erase the excess, because some cell may not draw text over + graph->rectangle(rectangle{ item_xpos + static_cast(header.pixels), y + 2, + ts.width + static_cast(content_pos)-header.pixels, essence_->item_size - 4 }, true); + } + extreme_text = std::max(extreme_text, item_xpos + content_pos + ts.width); } } - - using state = facade::state; - crook_renderer_.check(item.flags.checked ? state::checked : state::unchecked); - crook_renderer_.draw(*graph, bgcolor, fgcolor, essence_->checkarea(item_xpos, y), estate); } - if ((0 == index) && essence_->if_image) // draw the image if need, ??only before the first column?? (display_order=0 ?) - { - if (item.img) - { - nana::rectangle img_r(item.img_show_size); - img_r.x = static_cast(ext_w) + item_xpos + static_cast(16 - item.img_show_size.width) / 2; - img_r.y = y + static_cast(essence_->item_size - item.img_show_size.height) / 2; - item.img.stretch(item.img.size(), *graph, img_r); - } - ext_w += 18; - } + graph->line({ item_xpos - 1, y }, { item_xpos - 1, y + static_cast(essence_->item_size) - 1 }, static_cast(0xEBF4F9)); - graph->set_text_color(cell_txtcolor); - graph->string(point{ item_xpos + ext_w, y + txtoff }, m_cell.text); // draw full text of the cell index (column) - - if (ts.width + ext_w > header.pixels) // it was an excess - { - //The text is painted over the next subitem // here beging the ... - int xpos = item_xpos + static_cast(header.pixels) - static_cast(essence_->suspension_width); - - graph->set_color(it_bgcolor); // litter rect with the item bg end ... - graph->rectangle(rectangle{ xpos, y + 2, essence_->suspension_width, essence_->item_size - 4 }, true); - graph->set_text_color(cell_txtcolor); - graph->string(point{ xpos, y + 2 }, STR("...")); - - //Erase the part that over the next subitem. - if ( display_order + 1 < seqs.size() ) // this is not the last column - { - graph->set_color(bgcolor); // we need to erase the excess, because some cell may not draw text over - graph->rectangle(rectangle{item_xpos + static_cast(header.pixels), y + 2, - ts.width + ext_w - header.pixels, essence_->item_size - 4}, true); - } - extreme_text = std::max (extreme_text, item_xpos + ext_w + ts.width); - } } - graph->line({ item_xpos - 1, y }, { item_xpos - 1, y + static_cast(essence_->item_size) - 1 }, { 0xEB, 0xF4, 0xF9 }); - - item_xpos += header.pixels; + item_xpos += static_cast(header.pixels); if (display_order + 1 >= seqs.size() && static_cast(extreme_text) > item_xpos) { graph->set_color(item.bgcolor); graph->rectangle(rectangle{item_xpos , y + 2, extreme_text - item_xpos, essence_->item_size - 4}, true); } - first = false; } //Draw selecting inner rectangle if(item.flags.selected) - _m_draw_border(r.x, y, show_w); + _m_draw_border(content_r.x, y, show_w); + } + + essence_t::inline_pane * _m_get_inline_pane(const category_t& cat, std::size_t column_pos) const + { + if (column_pos < cat.factories.size()) + { + auto & factory = cat.factories[column_pos]; + if (factory) + return essence_->open_inline(factory.get(), cat.indicators[column_pos].get()); + } + return nullptr; + } + + essence_t::inline_pane* _m_find_inline_pane(const index_pair& pos, std::size_t column_pos) const + { + auto & cat = essence_->lister.at(pos.cat); + + if (column_pos >= cat.factories.size()) + return nullptr; + + auto& factory = cat.factories[column_pos]; + if (!factory) + return nullptr; + + auto i = essence_->inline_table.find(factory.get()); + if (i == essence_->inline_table.end()) + return nullptr; + + for (auto & inl_widget : i->second) + { + if (inl_widget->item_pos == pos && inl_widget->column_pos == column_pos) + return inl_widget.get(); + } + return nullptr; } void _m_draw_border(int x, int y, unsigned width) const @@ -3022,6 +3178,9 @@ namespace nana void trigger::draw() { + if (API::is_destroying(essence_->lister.wd_ptr()->handle())) + return; + nana::rectangle r; if(essence_->header.visible() && essence_->rect_header(r)) @@ -3134,15 +3293,12 @@ namespace nana } } - switch(update) + if (update) { - case 1: - API::update_window(essence_->lister.wd_ptr()->handle()); - break; - case 2: - draw(); + if (2 == update) + draw(); + API::lazy_refresh(); - break; } } @@ -3196,7 +3352,7 @@ namespace nana else if (arg.ctrl) sel = !item_proxy(essence_, index_pair (item_pos.cat, lister.absolute(item_pos))).selected(); else - lister.select_for_all(false); + lister.select_for_all(false); //cancel all selections } else sel = !item_proxy(essence_, index_pair (item_pos.cat, lister.absolute(item_pos))).selected(); @@ -3302,6 +3458,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; @@ -3370,21 +3534,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); @@ -3436,7 +3614,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? } } @@ -3469,6 +3647,7 @@ namespace nana m.flags.checked = ck; arg_listbox arg{*this, ck}; ess_->lister.wd_ptr()->events().checked.emit(arg); + ess_->update(); } return *this; } @@ -3481,7 +3660,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 @@ -3495,7 +3674,10 @@ namespace nana } else if (ess_->lister.last_selected_abs == pos_) ess_->lister.last_selected_abs.set_both(npos); + + ess_->update(); + ess_->update(); return *this; } @@ -3549,7 +3731,7 @@ namespace nana nana::string item_proxy::text(size_type col) const { - return ess_->lister.text(cat_, pos_.item, col); + return ess_->lister.get_cells(cat_, pos_.item).at(col).text; } void item_proxy::icon(const nana::paint::image& img) @@ -3569,17 +3751,18 @@ namespace nana //Behavior of Iterator's value_type bool item_proxy::operator==(const nana::string& s) const { - return (ess_->lister.text(cat_, pos_.item, 0) == s); + return (ess_->lister.get_cells(cat_, pos_.item).at(0).text == s); } bool item_proxy::operator==(const char * s) const { - return (ess_->lister.text(cat_, pos_.item, 0) == nana::string(nana::charset(s))); + return (ess_->lister.get_cells(cat_, pos_.item).at(0).text == nana::string(nana::charset(s))); + } bool item_proxy::operator==(const wchar_t * s) const { - return (ess_->lister.text(cat_, pos_.item, 0) == nana::string(nana::charset(s))); + return (ess_->lister.get_cells(cat_, pos_.item).at(0).text == nana::string(nana::charset(s))); } item_proxy & item_proxy::operator=(const item_proxy& rhs) @@ -3596,10 +3779,9 @@ namespace nana // Behavior of Iterator item_proxy & item_proxy::operator++() { - if (++pos_.item < cat_->items.size()) - return *this; + if (++pos_.item >= cat_->items.size()) + cat_ = nullptr; - cat_ = nullptr; return *this; } @@ -3715,6 +3897,23 @@ 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(); @@ -3757,6 +3956,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)); } @@ -3888,6 +4092,21 @@ namespace nana return ! this->operator==(r); } + void cat_proxy::inline_factory(size_type column, pat::cloneable> factory) + { + if (column >= ess_->header.cont().size()) + throw std::out_of_range("listbox.cat_proxy.inline_factory: invalid column index"); + + if (column >= cat_->factories.size()) + { + cat_->factories.resize(column + 1); + cat_->indicators.resize(column + 1); + } + + cat_->factories[column] = std::move(factory); + cat_->indicators[column].reset(new inline_indicator(ess_, column)); + } + void cat_proxy::_m_append(std::vector && cells) { //check invalid cells @@ -3938,7 +4157,6 @@ namespace nana arg_listbox::arg_listbox(const drawerbase::listbox::item_proxy& m, bool selected) : item(m), selected(selected) { - } //class listbox @@ -3957,11 +4175,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) @@ -3971,6 +4190,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 { @@ -3991,7 +4217,9 @@ namespace nana { internal_scope_guard lock; auto & ess = _m_ess(); - ess.lister.create_cat(args); + + for (auto & arg : args) + ess.lister.create_cat(nana::string{ arg }); ess.update(); } diff --git a/source/gui/widgets/menu.cpp b/source/gui/widgets/menu.cpp index 9f6ec24c..d42f2b79 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) @@ -737,21 +754,30 @@ namespace nana API::register_menu_window(this->handle(), !owner_menubar); } - events().destroy.connect_unignorable([this]{ + auto & events = this->events(); + events.destroy.connect_unignorable([this]{ _m_destroy(); }); - events().key_press.connect_unignorable([this](const arg_keyboard& arg){ + events.key_press.connect_unignorable([this](const arg_keyboard& arg){ _m_key_down(arg); }); - events().mouse_up.connect_unignorable([this]{ - pick(); - }); + auto fn = [this](const arg_mouse& arg) + { + if (event_code::mouse_down == arg.evt_code) + _m_open_sub(0); //Try to open submenu immediately + else if (event_code::mouse_up == arg.evt_code) + if (arg.button == ::nana::mouse::left_button) + pick(); + }; + + events.mouse_down.connect_unignorable(fn); + events.mouse_up.connect_unignorable(fn); timer_.interval(100); timer_.elapse([this]{ - this->_m_check_repeatly(); + this->_m_open_sub(500); //Try to open submenu }); timer_.start(); @@ -759,7 +785,7 @@ namespace nana if (want_focus_) { - event_focus_ = events().focus.connect_unignorable([this](const arg_focus& arg) + event_focus_ = events.focus.connect_unignorable([this](const arg_focus& arg) { //when the focus of the menu window is losing, close the menu. //But here is not every menu window may have focus event installed, @@ -797,29 +823,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 +989,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 +1042,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 +1304,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/menubar.cpp b/source/gui/widgets/menubar.cpp index 2c1de33d..203de8ec 100644 --- a/source/gui/widgets/menubar.cpp +++ b/source/gui/widgets/menubar.cpp @@ -148,7 +148,7 @@ namespace nana auto pos = items_->cont().size(); items_->append(text, shkey); - _m_draw(); + refresh(*graph_); API::update_window(*widget_); return at(pos); @@ -173,13 +173,65 @@ namespace nana widget_ = &widget; } - void trigger::refresh(graph_reference) + void trigger::refresh(graph_reference graph) { - _m_draw(); - API::lazy_refresh(); + auto bgcolor = API::bgcolor(*widget_); + graph_->rectangle(true, bgcolor); + + item_renderer ird(*widget_, graph); + + nana::point item_pos(2, 2); + nana::size item_s(0, 23); + + unsigned long index = 0; + for (auto i : items_->cont()) + { + //Transform the text if it contains the hotkey character + ::nana::char_t hotkey; + ::nana::string::size_type hotkey_pos; + auto text = API::transform_shortkey_text(i->text, hotkey, &hotkey_pos); + + nana::size text_s = graph.text_extent_size(text); + + item_s.width = text_s.width + 16; + + i->pos = item_pos; + i->size = item_s; + + using state = item_renderer::state; + state item_state = (index != state_.active ? state::normal : (state_.menu_active ? state::selected : state::highlighted)); + ird.background(item_pos, item_s, item_state); + + if (state::selected == item_state) + { + int x = item_pos.x + item_s.width; + int y1 = item_pos.y + 2, y2 = item_pos.y + item_s.height - 1; + graph.line({ x, y1 }, { x, y2 }, bgcolor.blend(colors::gray_border, 0.4)); + graph.line({ x + 1, y1 }, { x + 1, y2 }, bgcolor.blend(colors::button_face_shadow_end, 0.5)); + } + + //Draw text, the text is transformed from orignal for hotkey character + int text_top_off = (item_s.height - text_s.height) / 2; + ird.caption({ item_pos.x + 8, item_pos.y + text_top_off }, text); + + if (hotkey) + { + 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); + + unsigned ascent, descent, inleading; + graph.text_metrics(ascent, descent, inleading); + int x = item_pos.x + 8 + off_w; + int y = item_pos.y + text_top_off + ascent + 1; + graph.line({ x, y }, { x + static_cast(hotkey_size.width) - 1, y }, ::nana::colors::black); + } + + item_pos.x += i->size.width; + ++index; + } } - void trigger::mouse_move(graph_reference, const arg_mouse& arg) + void trigger::mouse_move(graph_reference graph, const arg_mouse& arg) { if (arg.pos != state_.mouse_pos) state_.nullify_mouse = false; @@ -200,7 +252,7 @@ namespace nana if(popup) { _m_popup_menu(); - _m_draw(); + refresh(graph); API::lazy_refresh(); } @@ -228,7 +280,7 @@ namespace nana else _m_total_close(); - _m_draw(); + refresh(graph); API::lazy_refresh(); } @@ -245,12 +297,12 @@ namespace nana { state_.behavior = state_.behavior_none; _m_total_close(); - _m_draw(); + refresh(graph); API::lazy_refresh(); } } - void trigger::focus(graph_reference, const arg_focus& arg) + void trigger::focus(graph_reference graph, const arg_focus& arg) { if((arg.getting == false) && (state_.active != npos)) { @@ -259,12 +311,12 @@ namespace nana state_.menu_active = false; _m_close_menu(); state_.active = npos; - _m_draw(); + refresh(graph); API::lazy_refresh(); } } - void trigger::key_press(graph_reference, const arg_keyboard& arg) + void trigger::key_press(graph_reference graph, const arg_keyboard& arg) { state_.nullify_mouse = true; if(state_.menu) @@ -354,11 +406,11 @@ namespace nana } } - _m_draw(); + refresh(graph); API::lazy_refresh(); } - void trigger::key_release(graph_reference, const arg_keyboard& arg) + void trigger::key_release(graph_reference graph, const arg_keyboard& arg) { if(arg.key == 18) { @@ -376,7 +428,7 @@ namespace nana } state_.menu_active = false; - _m_draw(); + refresh(graph); API::lazy_refresh(); } } @@ -396,7 +448,7 @@ namespace nana if(_m_popup_menu()) state_.menu->goto_next(true); - _m_draw(); + refresh(graph); API::lazy_refresh(); state_.behavior = state_.behavior_menu; } @@ -424,7 +476,7 @@ namespace nana if(index != state_.active) { state_.active = index; - _m_draw(); + refresh(*graph_); API::lazy_refresh(); if(_m_popup_menu()) @@ -456,7 +508,7 @@ namespace nana { _m_total_close(); - _m_draw(); + refresh(*graph_); API::update_window(widget_->handle()); } }); @@ -526,6 +578,7 @@ namespace nana return false; } + /* void trigger::_m_draw() { auto bgcolor = API::bgcolor(*widget_); @@ -583,6 +636,7 @@ namespace nana ++index; } } + */ //struct state_type trigger::state_type::state_type() diff --git a/source/gui/widgets/panel.cpp b/source/gui/widgets/panel.cpp index d360d916..8b09f6f0 100644 --- a/source/gui/widgets/panel.cpp +++ b/source/gui/widgets/panel.cpp @@ -20,10 +20,13 @@ namespace nana namespace panel { //class drawer - void drawer::attached(widget_reference widget, graph_reference) + void drawer::attached(widget_reference wdg, graph_reference) { - widget.caption(STR("Nana Panel")); - window_ = widget.handle(); + wdg.caption(STR("panel widget")); + window_ = wdg.handle(); + + API::ignore_mouse_focus(wdg, true); + } void drawer::refresh(graph_reference graph) diff --git a/source/gui/widgets/picture.cpp b/source/gui/widgets/picture.cpp index db067155..707edce5 100644 --- a/source/gui/widgets/picture.cpp +++ b/source/gui/widgets/picture.cpp @@ -13,6 +13,7 @@ */ #include +#include #include #include @@ -111,7 +112,7 @@ namespace nana _m_draw_background(fit_size.width, fit_size.height); - backimg.image.stretch(valid_area, graph, { pos, fit_size }); + backimg.image.stretch(valid_area, graph, ::nana::rectangle{ pos, fit_size }); } else { @@ -150,7 +151,7 @@ namespace nana _m_draw_background(graphsize.width, graphsize.height); color invalid_clr_for_call; - backimg.bground->draw(graph, invalid_clr_for_call, invalid_clr_for_call, graphsize, element_state::normal); + backimg.bground->draw(graph, invalid_clr_for_call, invalid_clr_for_call, rectangle{ graphsize }, element_state::normal); } graph.setsta(); @@ -170,7 +171,7 @@ namespace nana else if (bground.gradual_from == bground.gradual_to) graph->rectangle(true, bground.gradual_from); else - graph->gradual_rectangle(graph->size(), bground.gradual_from, bground.gradual_to, !bground.horizontal); + graph->gradual_rectangle(::nana::rectangle{ graph->size() }, bground.gradual_from, bground.gradual_to, !bground.horizontal); } } } diff --git a/source/gui/widgets/progress.cpp b/source/gui/widgets/progress.cpp index 4459ddb1..9f0bcf57 100644 --- a/source/gui/widgets/progress.cpp +++ b/source/gui/widgets/progress.cpp @@ -42,7 +42,7 @@ namespace nana if(_m_check_changing(value_)) { - _m_draw(); + refresh(*graph_); API::update_window(widget_->handle()); } return v; @@ -89,24 +89,28 @@ namespace nana { return unknown_; } - - void trigger::refresh(graph_reference) + bool trigger::stopped() const { - _m_draw(); + return stop_; + } + bool trigger::stop(bool s) + { + std::swap(s,stop_); + return s; } - void trigger::_m_draw() + void trigger::refresh(graph_reference graph) { - if(false == unknown_) - draw_width_ = static_cast((graph_->width() - border * 2) * (double(value_) / max_)); + if (false == unknown_) + draw_width_ = static_cast((graph.width() - border * 2) * (double(value_) / max_)); - _m_draw_box(*graph_); - _m_draw_progress(*graph_); + _m_draw_box(graph); + _m_draw_progress(graph); } void trigger::_m_draw_box(graph_reference graph) { - rectangle r = graph.size(); + rectangle r{ graph.size() }; graph.gradual_rectangle(r, colors::button_face_shadow_end, colors::button_face_shadow_start, true); ::nana::color lt{ colors::gray }, rb{colors::white}; graph.frame_rectangle(r, lt, lt, rb, rb); @@ -197,5 +201,13 @@ namespace nana { return get_drawer_trigger().unknown(); } + bool progress::stop(bool s) + { + return get_drawer_trigger().stop(s); + } + bool progress::stopped() const + { + return get_drawer_trigger().stopped(); + } //end class progress }//end namespace nana diff --git a/source/gui/widgets/scroll.cpp b/source/gui/widgets/scroll.cpp index b6d466fb..6c85a2ad 100644 --- a/source/gui/widgets/scroll.cpp +++ b/source/gui/widgets/scroll.cpp @@ -132,7 +132,7 @@ namespace nana _m_background(graph); - rectangle_rotator r(vertical_, graph.size()); + rectangle_rotator r(vertical_, ::nana::rectangle{ graph.size() }); r.x_ref() = static_cast(r.w() - fixedsize); r.w_ref() = fixedsize; @@ -159,7 +159,7 @@ namespace nana if (!metrics_.pressed || !_m_check()) return; - nana::rectangle_rotator r(vertical_, graph.size()); + nana::rectangle_rotator r(vertical_, ::nana::rectangle{ graph.size() }); if(metrics_.what == buttons::forward) { r.x_ref() = static_cast(fixedsize); @@ -253,7 +253,7 @@ namespace nana { if(_m_check()) { - rectangle_rotator r(vertical_, graph.size()); + rectangle_rotator r(vertical_, rectangle{ graph.size() }); r.x_ref() = static_cast(fixedsize + metrics_.scroll_pos); r.w_ref() = static_cast(metrics_.scroll_length); diff --git a/source/gui/widgets/skeletons/text_editor.cpp b/source/gui/widgets/skeletons/text_editor.cpp index 0a2f9261..e568fbee 100644 --- a/source/gui/widgets/skeletons/text_editor.cpp +++ b/source/gui/widgets/skeletons/text_editor.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -54,7 +55,6 @@ namespace nana{ namespace widgets virtual bool merge(const undoable_command_interface& rhs) override { - //Implement later return false; } protected: @@ -324,9 +324,8 @@ namespace nana{ namespace widgets pos.x = editor_._m_pixels_by_char(lnstr, pos.x) + editor_.text_area_.area.x; int pos_y = static_cast((pos.y - editor_.points_.offset.y) * editor_.line_height() + editor_._m_text_top_base()); - int pos_x = static_cast(pos.x - editor_.points_.offset.x); - return{ pos_x, pos_y }; + return{ static_cast(pos.x - editor_.points_.offset.x), pos_y }; } nana::upoint screen_to_caret(point scrpos) override @@ -461,6 +460,7 @@ namespace nana{ namespace widgets editor_._m_scrollbar(); return (adjusted_cond || adjusted_cond2); } + private: std::size_t _m_textline_from_screen(int y) const { @@ -1285,7 +1285,7 @@ namespace nana{ namespace widgets if (!API::widget_borderless(this->window_)) { ::nana::facade facade; - facade.draw(graph, bgcolor, API::fgcolor(this->window_), API::window_size(this->window_), API::element_state(this->window_)); + facade.draw(graph, bgcolor, API::fgcolor(this->window_), ::nana::rectangle{ API::window_size(this->window_) }, API::element_state(this->window_)); } }; } @@ -1349,8 +1349,9 @@ namespace nana{ namespace widgets attributes_.acceptive = acceptive; } - bool text_editor::respond_char(char_type key) //key is a character of ASCII code + bool text_editor::respond_char(const arg_keyboard& arg) //key is a character of ASCII code { + char_type key = arg.key; switch (key) { case keyboard::end_of_text: @@ -1400,14 +1401,28 @@ namespace nana{ namespace widgets return false; } - bool text_editor::respond_key(char_type key) + bool text_editor::respond_key(const arg_keyboard& arg) { + char_type key = arg.key; switch (key) { +#if 0 case keyboard::os_arrow_left: move_left(); break; case keyboard::os_arrow_right: move_right(); break; case keyboard::os_arrow_up: move_ns(true); break; case keyboard::os_arrow_down: move_ns(false); break; +#else + case keyboard::os_arrow_left: + case keyboard::os_arrow_right: + case keyboard::os_arrow_up: + case keyboard::os_arrow_down: + case keyboard::os_home: + case keyboard::os_end: + case keyboard::os_pageup: + case keyboard::os_pagedown: + _handle_move_key(arg); + break; +#endif case keyboard::os_del: if (this->attr().editable) del(); @@ -1482,6 +1497,8 @@ namespace nana{ namespace widgets behavior_->pre_calc_lines(width_pixels()); _m_scrollbar(); + + move_caret(points_.caret); return true; } @@ -1591,29 +1608,24 @@ namespace nana{ namespace widgets return true; } - bool text_editor::mouse_down(bool left_button, const point& scrpos) + bool text_editor::mouse_down(::nana::mouse button, const point& scrpos) { if (!hit_text_area(scrpos)) return false; - if(left_button) + if(::nana::mouse::left_button == button) { API::capture_window(window_, true); text_area_.captured = true; //Set caret pos by screen point and get the caret pos. - auto pos = mouse_caret(scrpos); - if(!hit_select_area(pos)) + mouse_caret(scrpos); + if(!select(false)) { - if(!select(false)) - { - select_.a = points_.caret; //Set begin caret - set_end_caret(); - } - select_.mode_selection = selection::mode_mouse_selected; + select_.a = points_.caret; //Set begin caret + set_end_caret(); } - else - select_.mode_selection = selection::mode_no_selected; + select_.mode_selection = selection::mode_mouse_selected; } text_area_.border_renderer(graph_, _m_bgcolor()); @@ -1644,7 +1656,7 @@ namespace nana{ namespace widgets return false; } - bool text_editor::mouse_up(bool left_button, const point& scrpos) + bool text_editor::mouse_up(::nana::mouse button, const point& scrpos) { auto is_prev_no_selected = (select_.mode_selection == selection::mode_no_selected); @@ -2217,7 +2229,137 @@ namespace nana{ namespace widgets points_.xpos = points_.caret.x; } - nana::upoint text_editor::mouse_caret(const point& scrpos) //From screen position + void text_editor::_handle_move_key(const arg_keyboard& arg) + { + bool changed = false; + nana::upoint caret = points_.caret; + char_t key = arg.key; + size_t nlines = textbase_.lines(); + if (arg.ctrl) { + switch (key) { + case keyboard::os_arrow_left: + case keyboard::os_arrow_right: + // TODO: move the caret word by word + break; + case keyboard::os_home: + if (caret.y != 0) { + caret.y = 0; + points_.offset.y = 0; + changed = true; + } + break; + case keyboard::os_end: + if (caret.y != nlines - 1) { + caret.y = nlines - 1; + changed = true; + } + break; + } + } + size_t lnsz = textbase_.getline(caret.y).size(); + switch (key) { + case keyboard::os_arrow_left: + if (caret.x != 0) { + --caret.x; + changed = true; + }else { + if (caret.y != 0) { + --caret.y; + caret.x = textbase_.getline(caret.y).size(); + changed = true; + } + } + break; + case keyboard::os_arrow_right: + if (caret.x < lnsz) { + ++caret.x; + changed = true; + }else { + if (caret.y != nlines - 1) { + ++caret.y; + caret.x = 0; + changed = true; + } + } + break; + case keyboard::os_arrow_up: + case keyboard::os_arrow_down: + { + auto screen_pt = behavior_->caret_to_screen(caret); + int offset = line_height(); + if (key == keyboard::os_arrow_up) { + offset = -offset; + } + screen_pt.y += offset; + auto new_caret = behavior_->screen_to_caret(screen_pt); + if (new_caret != caret) { + caret = new_caret; + if (screen_pt.y < 0) { + scroll(true, true); + } + changed = true; + } + } + break; + case keyboard::os_home: + if (caret.x != 0) { + caret.x = 0; + changed = true; + } + break; + case keyboard::os_end: + if (caret.x < lnsz) { + caret.x = lnsz; + changed = true; + } + break; + case keyboard::os_pageup: + if (caret.y >= screen_lines() && points_.offset.y >= static_cast(screen_lines())) { + points_.offset.y -= screen_lines(); + caret.y -= screen_lines(); + changed = true; + } + break; + case keyboard::os_pagedown: + if (caret.y + screen_lines() <= behavior_->take_lines()) { + points_.offset.y += screen_lines(); + caret.y += screen_lines(); + changed = true; + } + break; + } + if (select_.a != caret || select_.b != caret) { + changed = true; + } + if (changed) { + if (arg.shift) { + switch (key) { + case keyboard::os_arrow_left: + case keyboard::os_arrow_up: + case keyboard::os_home: + case keyboard::os_pageup: + select_.b = caret; + break; + case keyboard::os_arrow_right: + case keyboard::os_arrow_down: + case keyboard::os_end: + case keyboard::os_pagedown: + select_.b = caret; + break; + } + }else { + select_.b = caret; + select_.a = caret; + } + points_.caret = caret; + behavior_->adjust_caret_into_screen(); + render(true); + _m_scrollbar(); + points_.xpos = points_.caret.x; + } + } + + upoint text_editor::mouse_caret(const point& scrpos) //From screen position { points_.caret = behavior_->screen_to_caret(scrpos); @@ -2228,11 +2370,16 @@ namespace nana{ namespace widgets return points_.caret; } - nana::upoint text_editor::caret() const + upoint text_editor::caret() const { return points_.caret; } + point text_editor::caret_screen_pos() const + { + return behavior_->caret_to_screen(points_.caret); + } + bool text_editor::scroll(bool upwards, bool vert) { if(vert && attributes_.vscroll) @@ -2532,8 +2679,34 @@ namespace nana{ namespace widgets return true; } + std::size_t eat_endl(const wchar_t* str, std::size_t pos) + { + auto ch = str[pos]; + if (0 == ch) + return pos; + + const wchar_t * endlstr; + switch (ch) + { + case L'\n': + endlstr = L"\n\r"; + break; + case L'\r': + endlstr = L"\r\n"; + break; + default: + return pos; + } + + if (std::memcmp(str + pos, endlstr, sizeof(wchar_t) * 2) == 0) + return pos + 2; + + return pos + 1; + } + bool text_editor::_m_resolve_text(const nana::string& text, std::vector> & lines) { + auto const text_str = text.data(); std::size_t begin = 0; while (true) { @@ -2546,13 +2719,23 @@ namespace nana{ namespace widgets } lines.emplace_back(begin, pos); - begin = text.find_first_not_of(STR("\r\n"), pos + 1); + + pos = eat_endl(text_str, pos); + + begin = text.find_first_not_of(STR("\r\n"), pos); //The number of new lines minus one - const auto chp_end = text.data() + (begin == text.npos ? text.size() : begin); - for (auto chp = text.data() + (pos + 1); chp != chp_end; ++chp) - if (*chp == '\n') + const auto chp_end = text_str + (begin == text.npos ? text.size() : begin); + + for (auto chp = text_str + pos; chp != chp_end; ++chp) + { + auto eats = eat_endl(chp, 0); + if (eats) + { lines.emplace_back(0, 0); + chp += (eats - 1); + } + } if (text.npos == begin) { @@ -2740,50 +2923,43 @@ namespace nana{ namespace widgets const auto str_end = str + len; auto & entities = parser.entities(); - for (auto & ent : entities) + for (auto & ent : entities) + { + const ::nana::char_t* ent_begin = nullptr; + + int ent_off = 0; + if (str <= ent.begin && ent.begin < str_end) { - const ::nana::char_t* ent_begin = nullptr; + ent_begin = ent.begin; + ent_off = std::accumulate(glyphs, glyphs + (ent.begin - str), 0); + } + else if (ent.begin <= str && str < ent.end) + ent_begin = str; - int ent_off = 0; - if (str <= ent.begin && ent.begin < str_end) + if (ent_begin) + { + auto ent_end = (ent.end < str_end ? ent.end : str_end); + auto ent_pixels = std::accumulate(glyphs + (ent_begin - str), glyphs + (ent_end - str), unsigned{}); + + canvas.set_color(ent.scheme->bgcolor.invisible() ? _m_bgcolor() : ent.scheme->bgcolor); + canvas.set_text_color(ent.scheme->fgcolor.invisible() ? fgcolor : ent.scheme->fgcolor); + + canvas.rectangle(true); + + ent_pos.x += ent_off; + if (rtl) { - ent_begin = ent.begin; - ent_off = std::accumulate(glyphs, glyphs + (ent.begin - str), 0); + //draw the whole text if it is a RTL text, because Arbic language is transformable. + canvas.string({}, str, len); + graph_.bitblt(::nana::rectangle{ ent_pos, ::nana::size{ ent_pixels, canvas.height() } }, canvas, ::nana::point{ ent_off, 0 }); } - else if (ent.begin <= str && str < ent.end) - ent_begin = str; - - if (ent_begin) + else { - auto ent_end = (ent.end < str_end ? ent.end : str_end); - auto ent_pixels = std::accumulate(glyphs + (ent_begin - str), glyphs + (ent_end - str), unsigned{}); - - if (ent.scheme->bgcolor.invisible()) - canvas.set_color(_m_bgcolor()); - else - canvas.set_color(ent.scheme->bgcolor); - canvas.rectangle(true); - - if (ent.scheme->fgcolor.invisible()) - canvas.set_text_color(fgcolor); - else - canvas.set_text_color(ent.scheme->fgcolor); - - ent_pos.x += ent_off; - - if (rtl) - { - //draw the whole text if it is a RTL text, because Arbic language is transformable. - canvas.string({}, str, len); - graph_.bitblt({ ent_pos, ::nana::size{ ent_pixels, canvas.height() } }, canvas, ::nana::point{ ent_off, 0 }); - } - else - { - canvas.string({}, ent_begin, ent_end - ent_begin); - graph_.bitblt({ ent_pos, ::nana::size{ ent_pixels, canvas.height() } }, canvas); - } + canvas.string({}, ent_begin, ent_end - ent_begin); + graph_.bitblt(::nana::rectangle{ ent_pos, ::nana::size{ ent_pixels, canvas.height() } }, canvas); } } + } } void text_editor::_m_draw_string(int top, const ::nana::color& clr, const nana::upoint& str_pos, const nana::string& str, bool if_mask) const @@ -2795,6 +2971,7 @@ namespace nana{ namespace widgets if (if_mask && mask_char_) mask_str.reset(new nana::string(str.size(), mask_char_)); + bool focused = API::is_focus_ready(window_); auto & linestr = (if_mask && mask_char_ ? *mask_str : str); @@ -2817,7 +2994,7 @@ namespace nana{ namespace widgets graph_.set_color(scheme_->selection.get_color()); //The text is not selected or the whole line text is selected - if ((!_m_get_sort_select_points(a, b)) || (select_.a.y != str_pos.y && select_.b.y != str_pos.y)) + if (!focused || (!_m_get_sort_select_points(a, b)) || (select_.a.y != str_pos.y && select_.b.y != str_pos.y)) { bool selected = (a.y < str_pos.y && str_pos.y < b.y); for (auto & ent : reordered) @@ -2827,10 +3004,10 @@ namespace nana{ namespace widgets if ((text_pos.x + static_cast(str_w) > text_area_.area.x) && (text_pos.x < xend)) { - if (selected) + if (selected && focused) { graph_.set_text_color(scheme_->selection_text.get_color()); - graph_.rectangle({ text_pos, { str_w, line_h_pixels } }, true); + graph_.rectangle(::nana::rectangle{ text_pos, { str_w, line_h_pixels } }, true); graph_.string(text_pos, ent.begin, len); } else @@ -2839,7 +3016,7 @@ namespace nana{ namespace widgets text_pos.x += static_cast(str_w); } if (selected) - graph_.rectangle({ text_pos, { whitespace_w, line_h_pixels } }, true); + graph_.rectangle(::nana::rectangle{ text_pos, { whitespace_w, line_h_pixels } }, true); } else { @@ -2876,7 +3053,7 @@ namespace nana{ namespace widgets //selected all if (a.x <= pos && str_end <= b.x) { - graph_.rectangle({ text_pos, { str_w, line_h_pixels } }, true); + graph_.rectangle(::nana::rectangle{ text_pos, { str_w, line_h_pixels } }, true); graph_.set_text_color(scheme_->selection_text.get_color()); graph_.string(text_pos, ent.begin, len); } @@ -2906,7 +3083,7 @@ namespace nana{ namespace widgets part_pos.x += static_cast(head_w); //Draw selected part - graph_.rectangle({ part_pos, { sel_w, line_h_pixels } }, true); + graph_.rectangle(::nana::rectangle{ part_pos, { sel_w, line_h_pixels } }, true); graph_.set_text_color(scheme_->selection_text.get_color()); graph_.string(part_pos, ent.begin + (a.x - pos), endpos - a.x); @@ -2931,7 +3108,7 @@ namespace nana{ namespace widgets else { //LTR //Draw selected part - graph_.rectangle({ text_pos, { sel_w, line_h_pixels } }, true); + graph_.rectangle(::nana::rectangle{ text_pos, { sel_w, line_h_pixels } }, true); graph_.set_text_color(scheme_->selection_text.get_color()); graph_.string(text_pos, ent.begin, endpos - pos); @@ -2957,7 +3134,7 @@ namespace nana{ namespace widgets if (a.x < pos) { //Draw selected all - graph_.rectangle({ text_pos, { str_w, line_h_pixels } }, true, { 0x33, 0x99, 0xFF }); + graph_.rectangle(::nana::rectangle{ text_pos, { str_w, line_h_pixels } }, true, static_cast(0x3399FF)); graph_.set_text_color(scheme_->selection_text.get_color()); graph_.string(text_pos, ent.begin, len); } @@ -2978,7 +3155,7 @@ namespace nana{ namespace widgets ::nana::point part_pos{ text_pos.x + static_cast(head_w), text_pos.y }; //Draw selected part - graph_.rectangle({ part_pos, {str_w - head_w, line_h_pixels } }, true); + graph_.rectangle(::nana::rectangle{ part_pos, {str_w - head_w, line_h_pixels } }, true); graph_.set_text_color(scheme_->selection_text.get_color()); graph_.string(part_pos, ent.begin + a.x - pos, len - (a.x - pos)); } @@ -2991,7 +3168,7 @@ namespace nana{ namespace widgets if (str_pos.y < b.y) { if (a.y < str_pos.y || ((a.y == str_pos.y) && (a.x <= str_pos.x ))) - graph_.rectangle({ text_pos, { whitespace_w, line_h_pixels } }, true); + graph_.rectangle(::nana::rectangle{ text_pos, { whitespace_w, line_h_pixels } }, true); } } else if (b.y == str_pos.y) @@ -3007,7 +3184,7 @@ namespace nana{ namespace widgets if (pos + len <= b.x) { //Draw selected part - graph_.rectangle({ text_pos, { str_w, line_h_pixels } }, true); + graph_.rectangle(::nana::rectangle{ text_pos, { str_w, line_h_pixels } }, true); graph_.set_text_color(scheme_->selection_text.get_color()); graph_.string(text_pos, ent.begin, len); } @@ -3021,7 +3198,7 @@ namespace nana{ namespace widgets else { //draw selected part - graph_.rectangle({ text_pos, { sel_w, line_h_pixels } }, true); + graph_.rectangle(::nana::rectangle{ text_pos, { sel_w, line_h_pixels } }, true); graph_.set_text_color(scheme_->selection_text.get_color()); graph_.string(text_pos, ent.begin, b.x - pos); diff --git a/source/gui/widgets/slider.cpp b/source/gui/widgets/slider.cpp index 23af12a4..2e8ef4fc 100644 --- a/source/gui/widgets/slider.cpp +++ b/source/gui/widgets/slider.cpp @@ -55,7 +55,7 @@ namespace nana virtual void slider(window, graph_reference graph, const slider_t& s) { - nana::rectangle r = graph.size(); + nana::rectangle r{ graph.size() }; if(s.horizontal) { r.x = s.pos; @@ -66,7 +66,7 @@ namespace nana r.y = s.pos; r.height = s.scale; } - graph.round_rectangle(r, 3, 3, colors::black, true, {0xf0,0xf0,0xf0}); + graph.round_rectangle(r, 3, 3, colors::black, true, static_cast(0xf0f0f0)); } }; @@ -390,7 +390,7 @@ namespace nana nana::rectangle _m_bar_area() const { auto sz = other_.graph->size(); - nana::rectangle r = sz; + nana::rectangle r{ sz }; if(style::horizontal == attr_.dir) { r.x = attr_.slider_scale / 2 - attr_.border; diff --git a/source/gui/widgets/spinbox.cpp b/source/gui/widgets/spinbox.cpp index 6be8deb2..39083fd9 100644 --- a/source/gui/widgets/spinbox.cpp +++ b/source/gui/widgets/spinbox.cpp @@ -86,7 +86,7 @@ namespace nana ss >> v; if (v < begin_ || last_ < v) return false; - + diff = (value_ != v); value_ = v; return true; @@ -297,7 +297,7 @@ namespace nana bool diff; if (!range_->value(value_str, diff)) return false; - + if (diff) reset_text(); return true; @@ -371,9 +371,9 @@ namespace nana bool refreshed = false; if (pressed) - refreshed = editor_->mouse_down(arg.left_button, arg.pos); + refreshed = editor_->mouse_down(arg.button, arg.pos); else - refreshed = editor_->mouse_up(arg.left_button, arg.pos); + refreshed = editor_->mouse_up(arg.button, arg.pos); if (refreshed) _m_draw_spins(buttons::none); @@ -403,7 +403,7 @@ namespace nana _m_draw_spins(buttons::none); return true; } - + return false; } @@ -435,7 +435,7 @@ namespace nana auto size = API::window_size(editor_->window_handle()); if (size.width > 18) return{ static_cast(size.width - 16), 0, 16, size.height }; - + return{ 0, 0, size.width, size.height }; } @@ -513,7 +513,7 @@ namespace nana { impl_->attach(wdg, graph); } - + void drawer::refresh(graph_reference) { impl_->render(); @@ -526,7 +526,7 @@ namespace nana impl_->editor()->reset_caret(); API::lazy_refresh(); } - + void drawer::mouse_wheel(graph_reference, const arg_wheel& arg) { impl_->mouse_wheel(arg.upwards); @@ -557,10 +557,10 @@ namespace nana impl_->render(); API::lazy_refresh(); } - + void drawer::key_press(graph_reference, const arg_keyboard& arg) { - if (impl_->editor()->respond_key(arg.key)) + if (impl_->editor()->respond_key(arg)) { impl_->editor()->reset_caret(); impl_->draw_spins(); @@ -570,7 +570,7 @@ namespace nana void drawer::key_char(graph_reference, const arg_keyboard& arg) { - if (impl_->editor()->respond_char(arg.key)) + if (impl_->editor()->respond_char(arg)) { if (!impl_->value(impl_->editor()->text())) impl_->draw_spins(); @@ -682,7 +682,7 @@ namespace nana modifier(static_cast(::nana::charset(prefix_utf8, ::nana::unicode::utf8)), static_cast(::nana::charset(suffix_utf8, ::nana::unicode::utf8))); } - ::nana::string spinbox::_m_caption() const + ::nana::string spinbox::_m_caption() const throw() { internal_scope_guard lock; auto editor = get_drawer_trigger().impl()->editor(); diff --git a/source/gui/widgets/tabbar.cpp b/source/gui/widgets/tabbar.cpp index 9d69b7b6..7a4aaf02 100644 --- a/source/gui/widgets/tabbar.cpp +++ b/source/gui/widgets/tabbar.cpp @@ -947,7 +947,7 @@ namespace nana auto bgcolor = API::bgcolor(basis_.wd); auto fgcolor = API::fgcolor(basis_.wd); - item_renderer::item_t m = { basis_.graph->size() }; + item_renderer::item_t m{ ::nana::rectangle{ basis_.graph->size() } }; basis_.renderer->background(*basis_.graph, m.r, bgcolor); @@ -985,7 +985,7 @@ namespace nana } if(false == item.img.empty()) - item.img.stretch(item.img.size(), *basis_.graph, nana::rectangle(m.r.x + 4, (m.r.height - 16) / 2, 16, 16)); + item.img.stretch(::nana::rectangle{ item.img.size() }, *basis_.graph, nana::rectangle(m.r.x + 4, (m.r.height - 16) / 2, 16, 16)); if(item.text.size()) { diff --git a/source/gui/widgets/textbox.cpp b/source/gui/widgets/textbox.cpp index 4bb6a670..59e51d57 100644 --- a/source/gui/widgets/textbox.cpp +++ b/source/gui/widgets/textbox.cpp @@ -15,6 +15,9 @@ #include #include +#include +#include + namespace nana { arg_textbox::arg_textbox(textbox& wdg) @@ -89,7 +92,11 @@ namespace drawerbase { void drawer::focus(graph_reference graph, const arg_focus& arg) { refresh(graph); - + if (!editor_->attr().multi_lines && arg.getting) + { + editor_->select(true); + editor_->move_caret_end(); + } editor_->show_caret(arg.getting); editor_->reset_caret(); API::lazy_refresh(); @@ -97,7 +104,7 @@ namespace drawerbase { void drawer::mouse_down(graph_reference, const arg_mouse& arg) { - if(editor_->mouse_down(arg.left_button, arg.pos)) + if(editor_->mouse_down(arg.button, arg.pos)) API::lazy_refresh(); } @@ -109,7 +116,7 @@ namespace drawerbase { void drawer::mouse_up(graph_reference graph, const arg_mouse& arg) { - if(editor_->mouse_up(arg.left_button, arg.pos)) + if(editor_->mouse_up(arg.button, arg.pos)) API::lazy_refresh(); } @@ -136,7 +143,7 @@ namespace drawerbase { void drawer::key_press(graph_reference, const arg_keyboard& arg) { - if(editor_->respond_key(arg.key)) + if(editor_->respond_key(arg)) { editor_->reset_caret(); API::lazy_refresh(); @@ -145,7 +152,7 @@ namespace drawerbase { void drawer::key_char(graph_reference, const arg_keyboard& arg) { - if (editor_->respond_char(arg.key)) + if (editor_->respond_char(arg)) API::lazy_refresh(); } @@ -287,6 +294,26 @@ namespace drawerbase { return (editor ? editor->getline(line_index, text) : false); } + /// Gets the caret position + bool textbox::caret_pos(point& pos, bool text_coordinate) const + { + internal_scope_guard lock; + auto editor = get_drawer_trigger().editor(); + + auto scr_pos = editor->caret_screen_pos(); + + if (text_coordinate) + { + auto upos = editor->caret(); + pos.x = static_cast(upos.x); + pos.y = static_cast(upos.y); + } + else + pos = scr_pos; + + return editor->hit_text_area(scr_pos); + } + textbox& textbox::append(const nana::string& text, bool at_caret) { internal_scope_guard lock; @@ -508,7 +535,7 @@ namespace drawerbase { } //Override _m_caption for caption() - nana::string textbox::_m_caption() const + nana::string textbox::_m_caption() const throw() { internal_scope_guard lock; auto editor = get_drawer_trigger().editor(); diff --git a/source/gui/widgets/toolbar.cpp b/source/gui/widgets/toolbar.cpp index 3936cfce..15de2fff 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(size, graph, pos); + item.image.paste(::nana::rectangle{ imgsize }, graph, pos); if(item.enable == false) { - nana::paint::graphics gh(size); - gh.bitblt(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 = 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(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/gui/widgets/treebox.cpp b/source/gui/widgets/treebox.cpp index a4e51a0b..9362b243 100644 --- a/source/gui/widgets/treebox.cpp +++ b/source/gui/widgets/treebox.cpp @@ -27,7 +27,7 @@ namespace nana //Here defines some function objects namespace treebox { - typedef trigger::node_type node_type; + using node_type = trigger::node_type; bool no_sensitive_compare(const nana::string& text, const nana::char_t *pattern, std::size_t len) { @@ -56,7 +56,7 @@ namespace nana node = node->child; while(node) { - if(no_sensitive_compare(node->value.second.text, pattern, len)) return node; + if(no_sensitive_compare(node->value.second.text, pattern, len)) return node; if(node == end) break; @@ -78,7 +78,7 @@ namespace nana : public drawer_trigger, public compset_interface { public: - typedef drawer_trigger::graph_reference graph_reference; + using graph_reference = drawer_trigger::graph_reference; void assign(const item_attribute_t & item_attr, const pat::cloneable* renderer, const pat::cloneable * compset_placer) { @@ -583,7 +583,7 @@ namespace nana void event_scrollbar(const arg_mouse& arg) { - if((event_code::mouse_wheel == arg.evt_code) || arg.left_button) + if((event_code::mouse_wheel == arg.evt_code) || arg.is_left_button()) { if(shape.prev_first_value != shape.scroll.value()) { @@ -719,17 +719,29 @@ namespace nana node_state.tooltip->impl().assign(node_attr, &data.renderer, &data.comp_placer); node_state.tooltip->show(); - auto & events = node_state.tooltip->events(); - events.mouse_leave.connect([this](const arg_mouse&){ - this->close_tooltip_window(); - }); - events.mouse_move.connect([this](const arg_mouse&){ - this->mouse_move_tooltip_window(); - }); - - auto fn = [this](const arg_mouse& arg){ - this->click_tooltip_window(arg); + auto fn = [this](const arg_mouse& arg) + { + switch (arg.evt_code) + { + case event_code::mouse_leave: + close_tooltip_window(); + break; + case event_code::mouse_move: + mouse_move_tooltip_window(); + break; + case event_code::mouse_down: + case event_code::mouse_up: + case event_code::dbl_click: + click_tooltip_window(arg); + break; + default: //ignore other events + break; + } }; + + auto & events = node_state.tooltip->events(); + events.mouse_leave(fn); + events.mouse_move(fn); events.mouse_down.connect(fn); events.mouse_up.connect(fn); events.dbl_click.connect(fn); @@ -1241,13 +1253,14 @@ namespace nana if(compset->comp_attribute(component::icon, attr)) { const nana::paint::image * img = nullptr; - if(compset->item_attribute().mouse_pointed) - img = &(compset->item_attribute().icon_hover); - else if(compset->item_attribute().expended) - img = &(compset->item_attribute().icon_expanded); + auto & item_attr = compset->item_attribute(); + if (item_attr.mouse_pointed) + img = &(item_attr.icon_hover); + else if (item_attr.expended) + img = &(item_attr.icon_expanded); if((nullptr == img) || img->empty()) - img = &(compset->item_attribute().icon_normal); + img = &(item_attr.icon_normal); if(! img->empty()) { @@ -1260,10 +1273,10 @@ namespace nana attr.area.x += (attr.area.width - fit_size.width) / 2; attr.area.y += (attr.area.height - fit_size.height) / 2; attr.area = fit_size; - img->stretch(size, graph, attr.area); + img->stretch(::nana::rectangle{ size }, graph, attr.area); } else - img->paste(graph, attr.area.x + static_cast(attr.area.width - size.width) / 2, attr.area.y + static_cast(attr.area.height - size.height) / 2); + img->paste(graph, point{ attr.area.x + static_cast(attr.area.width - size.width) / 2, attr.area.y + static_cast(attr.area.height - size.height) / 2 }); } } } @@ -1272,12 +1285,8 @@ namespace nana { comp_attribute_t attr; if (compset->comp_attribute(component::text, attr)) - { - graph.set_text_color(fgcolor_); - graph.string(point{ attr.area.x, attr.area.y + 3 }, compset->item_attribute().text); - } + graph.string(point{ attr.area.x, attr.area.y + 3 }, compset->item_attribute().text, fgcolor_); } - private: mutable facade crook_; }; diff --git a/source/gui/widgets/widget.cpp b/source/gui/widgets/widget.cpp index 07e81946..af853b38 100644 --- a/source/gui/widgets/widget.cpp +++ b/source/gui/widgets/widget.cpp @@ -1,9 +1,9 @@ /* * The fundamental widget class implementation - * Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com) * - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) * * @file: nana/gui/widgets/widget.cpp @@ -11,6 +11,7 @@ #include #include +#include namespace nana { @@ -18,9 +19,42 @@ namespace nana { void set_eval(window, i18n_eval&&); } + //class widget //@brief:The definition of class widget - nana::string widget::caption() const + class widget::notifier: public detail::widget_notifier_interface + { + public: + notifier(widget& wdg) + : wdg_(wdg) + {} + + private: + //implementation of widget_notifier_interface + widget* widget_ptr() const override + { + return &wdg_; + } + + void destroy() override + { + wdg_._m_notify_destroy(); + } + + std::wstring caption() override + { + return wdg_._m_caption(); + } + + virtual void caption(std::wstring text) + { + wdg_._m_caption(std::move(text)); + } + private: + widget& wdg_; + }; + + nana::string widget::caption() const throw() { return this->_m_caption(); } @@ -30,7 +64,7 @@ namespace nana _m_caption(std::wstring(::nana::charset(utf8, ::nana::unicode::utf8))); } - void widget::caption(nana::string str) + void widget::caption(std::wstring str) { _m_caption(std::move(str)); } @@ -91,7 +125,7 @@ namespace nana bool widget::empty() const { - return (nullptr == handle()); + return (nullptr == handle()); } void widget::focus() @@ -139,6 +173,11 @@ namespace nana _m_move(x, y); } + void widget::move(const point& pos) + { + _m_move(pos.x, pos.y); + } + void widget::move(const rectangle& r) { _m_move(r); @@ -205,10 +244,15 @@ namespace nana return handle(); } + std::unique_ptr<::nana::detail::widget_notifier_interface> widget::_m_wdg_notifier() + { + return std::unique_ptr<::nana::detail::widget_notifier_interface>(new notifier(*this)); + } + void widget::_m_complete_creation() {} - nana::string widget::_m_caption() const + nana::string widget::_m_caption() const throw() { return API::dev::window_caption(handle()); } @@ -298,7 +342,14 @@ namespace nana { return API::bgcolor(handle()); } - //end class widget + + namespace detail + { + std::unique_ptr widget_notifier_interface::get_notifier(widget* wdg) + { + return std::unique_ptr(new widget::notifier(*wdg)); + } + } }//end namespace nana diff --git a/include/nana/paint/detail/image_bmp.hpp b/source/paint/detail/image_bmp.hpp similarity index 92% rename from include/nana/paint/detail/image_bmp.hpp rename to source/paint/detail/image_bmp.hpp index 5397faef..f8b08848 100644 --- a/include/nana/paint/detail/image_bmp.hpp +++ b/source/paint/detail/image_bmp.hpp @@ -13,7 +13,7 @@ #ifndef NANA_PAINT_DETAIL_IMAGE_BMP_HPP #define NANA_PAINT_DETAIL_IMAGE_BMP_HPP -#include "image_impl_interface.hpp" +#include #include namespace nana{ namespace paint @@ -25,24 +25,24 @@ namespace nana{ namespace paint struct bitmap_file_header { unsigned short bfType; - unsigned long bfSize; + unsigned bfSize; unsigned short bfReserved1; unsigned short bfReserved2; - unsigned long bfOffBits; + unsigned bfOffBits; } __attribute__((packed)); struct bitmap_info_header { - unsigned long biSize; - long biWidth; - long biHeight; + unsigned biSize; + int biWidth; + int biHeight; unsigned short biPlanes; unsigned short biBitCount; - unsigned long biCompression; - unsigned long biSizeImage; - long biXPelsPerMeter; - long biYPelsPerMeter; - unsigned long biClrUsed; - unsigned long biClrImportant; + unsigned biCompression; + unsigned biSizeImage; + int biXPelsPerMeter; + int biYPelsPerMeter; + unsigned biClrUsed; + unsigned biClrImportant; }__attribute__((packed)); struct rgb_quad @@ -75,7 +75,13 @@ namespace nana{ namespace paint this->close(); } - bool open(const nana::char_t* filename) + bool open(const void* data, std::size_t bytes) override + { + // TODO: read a BMP file from memory + return false; + } + + bool open(const nana::char_t* filename) override { if(nullptr == filename) return false; std::ifstream ifs; @@ -330,33 +336,33 @@ namespace nana{ namespace paint return (false == pixbuf_.empty()); } - bool alpha_channel() const + bool alpha_channel() const override { return false; } - bool empty() const + bool empty() const override { return pixbuf_.empty(); } - void close() + void close() override { pixbuf_.close(); } - nana::size size() const + nana::size size() const override { return pixbuf_.size(); } - void paste(const nana::rectangle& src_r, graph_reference graph, int x, int y) const + void paste(const nana::rectangle& src_r, graph_reference graph, const point& p_dst) const override { if(graph && pixbuf_) - pixbuf_.paste(src_r, graph.handle(), x, y); + pixbuf_.paste(src_r, graph.handle(), p_dst); } - void stretch(const nana::rectangle& src_r, graph_reference graph, const nana::rectangle& r) const + void stretch(const nana::rectangle& src_r, graph_reference graph, const nana::rectangle& r) const override { if(graph && pixbuf_) pixbuf_.stretch(src_r, graph.handle(), r); diff --git a/include/nana/paint/detail/image_ico.hpp b/source/paint/detail/image_ico.hpp similarity index 65% rename from include/nana/paint/detail/image_ico.hpp rename to source/paint/detail/image_ico.hpp index 8c66d19b..5be39454 100644 --- a/include/nana/paint/detail/image_ico.hpp +++ b/source/paint/detail/image_ico.hpp @@ -1,7 +1,7 @@ #ifndef NANA_PAINT_DETAIL_IMAGE_ICO_HPP #define NANA_PAINT_DETAIL_IMAGE_ICO_HPP -#include "image_impl_interface.hpp" +#include namespace nana{ namespace paint { @@ -22,14 +22,15 @@ namespace nana{ namespace paint public: image_ico(bool is_ico); - bool open(const nana::char_t* filename); - bool alpha_channel() const; - bool empty() const; - void close(); - nana::size size() const; - virtual void paste(const nana::rectangle& src_r, graph_reference graph, int x, int y) const; - virtual void stretch(const nana::rectangle&, graph_reference graph, const nana::rectangle& r) const; + bool open(const nana::char_t* filename) override; + bool open(const void* data, std::size_t bytes) override; + bool alpha_channel() const override; + bool empty() const override; + void close() override; + nana::size size() const override; + virtual void paste(const nana::rectangle& src_r, graph_reference graph, const point& p_dst) const override; + virtual void stretch(const nana::rectangle&, graph_reference graph, const nana::rectangle& r) const override; const ptr_t & ptr() const; private: const bool is_ico_; diff --git a/include/nana/paint/detail/image_png.hpp b/source/paint/detail/image_png.hpp similarity index 91% rename from include/nana/paint/detail/image_png.hpp rename to source/paint/detail/image_png.hpp index 9f54e8ef..cab432cd 100644 --- a/include/nana/paint/detail/image_png.hpp +++ b/source/paint/detail/image_png.hpp @@ -1,7 +1,7 @@ #ifndef NANA_PAINT_DETAIL_IMAGE_PNG_HPP #define NANA_PAINT_DETAIL_IMAGE_PNG_HPP -#include "image_impl_interface.hpp" +#include //Separate the libpng from the package that system provides. #if defined(NANA_LIBPNG) @@ -11,7 +11,7 @@ #endif #include -#include "../pixel_buffer.hpp" +#include namespace nana { @@ -25,7 +25,7 @@ namespace nana { } - bool open(const nana::char_t* png_file) + bool open(const nana::char_t* png_file) override { #ifdef NANA_UNICODE FILE * fp = ::fopen(static_cast(nana::charset(png_file)).c_str(), "rb"); @@ -148,32 +148,32 @@ namespace nana return is_opened; } - bool alpha_channel() const + bool alpha_channel() const override { return pixbuf_.alpha_channel(); } - virtual bool empty() const + virtual bool empty() const override { return pixbuf_.empty(); } - virtual void close() + virtual void close() override { pixbuf_.close(); } - virtual nana::size size() const + virtual nana::size size() const override { return pixbuf_.size(); } - void paste(const nana::rectangle& src_r, graph_reference graph, int x, int y) const + void paste(const nana::rectangle& src_r, graph_reference graph, const point& p_dst) const override { - pixbuf_.paste(src_r, graph.handle(), x, y); + pixbuf_.paste(src_r, graph.handle(), p_dst); } - void stretch(const nana::rectangle& src_r, graph_reference dst, const nana::rectangle& r) const + void stretch(const nana::rectangle& src_r, graph_reference dst, const nana::rectangle& r) const override { pixbuf_.stretch(src_r, dst.handle(), r); } diff --git a/source/paint/detail/native_paint_interface.cpp b/source/paint/detail/native_paint_interface.cpp index 7c96f7b6..153ac5f0 100644 --- a/source/paint/detail/native_paint_interface.cpp +++ b/source/paint/detail/native_paint_interface.cpp @@ -98,13 +98,13 @@ namespace detail return bgcolor; } - void blend(drawable_type dw, const nana::rectangle& area, pixel_color_t color, double fade_rate) + void blend(drawable_type dw, const rectangle& area, pixel_color_t color, double fade_rate) { - if(fade_rate <= 0) return; - if(fade_rate > 1) fade_rate = 1; + if (fade_rate <= 0) return; + if (fade_rate > 1) fade_rate = 1; - nana::rectangle r; - if(false == nana::overlap(drawable_size(dw), area, r)) + rectangle r; + if (false == ::nana::overlap(rectangle{ drawable_size(dw) }, area, r)) return; unsigned red = static_cast((color.value & 0xFF0000) * fade_rate); @@ -114,11 +114,11 @@ namespace detail double lrate = 1 - fade_rate; pixel_buffer pixbuf(dw, r.y, r.height); - for(std::size_t row = 0; row < r.height; ++row) + for (std::size_t row = 0; row < r.height; ++row) { auto i = pixbuf.raw_ptr(row) + r.x; const auto end = i + r.width; - for(; i < end; ++i) + for (; i < end; ++i) { unsigned px_r = ((static_cast((i->value & 0xFF0000) * lrate) + red) & 0xFF0000); unsigned px_g = ((static_cast((i->value & 0xFF00) * lrate) + green) & 0xFF00); @@ -126,7 +126,7 @@ namespace detail i->value = (px_r | px_g | px_b); } } - pixbuf.paste(nana::rectangle(r.x, 0, r.width, r.height), dw, r.x, r.y); + pixbuf.paste(nana::rectangle(r.x, 0, r.width, r.height), dw, point{r.x, r.y}); } nana::size raw_text_extent_size(drawable_type dw, const nana::char_t* text, std::size_t len) @@ -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; diff --git a/source/paint/graphics.cpp b/source/paint/graphics.cpp index 8e61e8dd..70bba1ef 100644 --- a/source/paint/graphics.cpp +++ b/source/paint/graphics.cpp @@ -604,7 +604,7 @@ namespace paint if(dst.handle_ && handle_ && (dst.handle_ != handle_)) { pixel_buffer s_pixbuf; - s_pixbuf.attach(handle_, size()); + s_pixbuf.attach(handle_, ::nana::rectangle{ size() }); s_pixbuf.blend(s_r, dst.handle_, d_pos, fade_rate); @@ -618,7 +618,7 @@ namespace paint { pixel_buffer pixbuf(handle_, 0, 0); pixbuf.blur(r, radius); - pixbuf.paste(handle_, 0, 0); + pixbuf.paste(handle_, point{}); } } @@ -677,7 +677,7 @@ namespace paint } delete [] tablebuf; - pixbuf.paste(handle_, 0, 0); + pixbuf.paste(handle_, point{}); if(changed_ == false) changed_ = true; } } @@ -812,7 +812,7 @@ namespace paint size_.width = size_.height = 0; } - void graphics::save_as_file(const char* file) + void graphics::save_as_file(const char* file) const { if(handle_) { @@ -1022,13 +1022,13 @@ namespace paint void graphics::rectangle(bool solid) { - rectangle(size(), solid); + rectangle(::nana::rectangle{ size() }, solid); } void graphics::rectangle(bool solid, const ::nana::color& clr) { set_color(clr); - rectangle(size(), solid); + rectangle(::nana::rectangle{ size() }, solid); } void graphics::rectangle(const ::nana::rectangle& r, bool solid) @@ -1074,7 +1074,7 @@ namespace paint if (pxbuf_.open(handle_)) { pxbuf_.gradual_rectangle(rct, from, to, 0.0, vertical); - pxbuf_.paste(handle_, 0, 0); + pxbuf_.paste(handle_, point{}); } #elif defined(NANA_X11) if (nullptr == handle_) return; diff --git a/source/paint/image.cpp b/source/paint/image.cpp index a6fcd598..f27a55c9 100644 --- a/source/paint/image.cpp +++ b/source/paint/image.cpp @@ -16,14 +16,15 @@ #include #include #include +#include #include #include #if defined(NANA_ENABLE_PNG) - #include + #include "detail/image_png.hpp" #endif -#include -#include +#include "detail/image_bmp.hpp" +#include "detail/image_ico.hpp" namespace nana { @@ -69,6 +70,31 @@ namespace paint return false; } + bool image_ico::open(const void* data, std::size_t bytes) + { + close(); +#if defined(NANA_WINDOWS) + HICON handle = ::CreateIconFromResource((PBYTE)data, static_cast(bytes), TRUE, 0x00030000); + if(handle) + { + ICONINFO info; + if (::GetIconInfo(handle, &info) != 0) + { + HICON * p = new HICON(handle); + ptr_ = std::shared_ptr(p, handle_deleter()); + size_.width = (info.xHotspot << 1); + size_.height = (info.yHotspot << 1); + ::DeleteObject(info.hbmColor); + ::DeleteObject(info.hbmMask); + return true; + } + } +#else + if(is_ico_){} //kill the unused compiler warning in Linux. +#endif + return false; + } + bool image_ico::alpha_channel() const { return false; @@ -89,12 +115,12 @@ namespace paint return size_; } - void image_ico::paste(const nana::rectangle& src_r, graph_reference graph, int x, int y) const + void image_ico::paste(const nana::rectangle& src_r, graph_reference graph, const point& p_dst) const { if(ptr_ && (graph.empty() == false)) { #if defined(NANA_WINDOWS) - ::DrawIconEx(graph.handle()->context, x, y, *ptr_, src_r.width, src_r.height, 0, 0, DI_NORMAL); + ::DrawIconEx(graph.handle()->context, p_dst.x, p_dst.y, *ptr_, src_r.width, src_r.height, 0, 0, DI_NORMAL); #endif } } @@ -235,6 +261,13 @@ namespace paint return false; } + bool image::open_icon(const void* data, std::size_t bytes) + { + image::image_impl_interface * helper = new detail::image_ico(true); + image_ptr_ = std::shared_ptr(helper); + return helper->open(data, bytes); + } + bool image::empty() const { return ((nullptr == image_ptr_) || image_ptr_->empty()); @@ -260,22 +293,35 @@ namespace paint return (image_ptr_ ? image_ptr_->size() : nana::size()); } - void image::paste(graphics& dst, int x, int y) const + void image::paste(graphics& dst, const point& p_dst) const { - if(image_ptr_) - image_ptr_->paste(image_ptr_->size(), dst, x, y); + this->paste(rectangle{ this->size() }, dst, p_dst); } - void image::paste(const nana::rectangle& r_src, graphics & dst, const nana::point& p_dst) const + void image::paste(const rectangle& r_src, graphics & dst, const point& p_dst) const { - if(image_ptr_) - image_ptr_->paste(r_src, dst, p_dst.x, p_dst.y); + if (image_ptr_) + { + if (dst.empty()) + dst.make({ r_src.width, r_src.height }); //throws if failed to create + + image_ptr_->paste(r_src, dst, p_dst); + } + else + throw std::runtime_error("image is empty"); } void image::stretch(const nana::rectangle& r_src, graphics& dst, const nana::rectangle & r_dst) const { - if(image_ptr_) - image_ptr_->stretch(r_src, dst, r_dst); + if (image_ptr_) + { + if (dst.empty()) + dst.make({ r_src.width, r_src.height }); //throws if failed to create + + image_ptr_->stretch(r_src, dst, r_dst); + } + else + throw std::runtime_error("image is empty"); } //end class image diff --git a/source/paint/pixel_buffer.cpp b/source/paint/pixel_buffer.cpp index bbb34b59..3c3a47c9 100644 --- a/source/paint/pixel_buffer.cpp +++ b/source/paint/pixel_buffer.cpp @@ -22,10 +22,10 @@ namespace nana{ namespace paint { - nana::rectangle valid_rectangle(const nana::size& s, const nana::rectangle& r) + nana::rectangle valid_rectangle(const size& s, const rectangle& r) { nana::rectangle good_r; - nana::overlap(s, r, good_r); + nana::overlap(rectangle{ s }, r, good_r); return good_r; } @@ -384,13 +384,13 @@ namespace nana{ namespace paint close(); } - void pixel_buffer::attach(drawable_type drawable, const nana::rectangle& want_r) + void pixel_buffer::attach(drawable_type drawable, const ::nana::rectangle& want_r) { storage_.reset(); if(drawable) { nana::rectangle r; - if(nana::overlap(nana::paint::detail::drawable_size(drawable), want_r, r)) + if (::nana::overlap(::nana::rectangle{ nana::paint::detail::drawable_size(drawable) }, want_r, r)) storage_ = std::make_shared(drawable, r); } } @@ -417,7 +417,7 @@ namespace nana{ namespace paint #elif defined(NANA_X11) try { - storage_ = std::make_shared(drawable, sz); + storage_ = std::make_shared(drawable, nana::rectangle{sz}); storage_->detach(); return true; } @@ -429,16 +429,16 @@ namespace nana{ namespace paint bool pixel_buffer::open(drawable_type drawable, const nana::rectangle & want_rectangle) { - nana::size sz = nana::paint::detail::drawable_size(drawable); + auto sz = nana::paint::detail::drawable_size(drawable); if(want_rectangle.x >= static_cast(sz.width) || want_rectangle.y >= static_cast(sz.height)) return false; - nana::rectangle want_r = want_rectangle; + auto want_r = want_rectangle; if(want_r.width == 0) want_r.width = sz.width - want_r.x; if(want_r.height == 0) want_r.height = sz.height - want_r.y; - nana::rectangle r; - if(false == overlap(sz, want_r, r)) + ::nana::rectangle r; + if (false == overlap(::nana::rectangle{ sz }, want_r, r)) return false; #if defined(NANA_WINDOWS) BITMAPINFO bmpinfo; @@ -633,13 +633,13 @@ namespace nana{ namespace paint *reinterpret_cast(reinterpret_cast(sp->raw_pixel_buffer + x) + y * sp->bytes_per_line) = px; } - void pixel_buffer::paste(drawable_type drawable, int x, int y) const + void pixel_buffer::paste(drawable_type drawable, const point& p_dst) const { if(storage_) - paste(nana::rectangle(storage_->pixel_size), drawable, x, y); + paste(nana::rectangle(storage_->pixel_size), drawable, p_dst); } - void pixel_buffer::paste(const nana::rectangle& src_r, drawable_type drawable, int x, int y) const + void pixel_buffer::paste(const nana::rectangle& src_r, drawable_type drawable, const point& p_dst) const { auto sp = storage_.get(); if(drawable && sp) @@ -647,7 +647,7 @@ namespace nana{ namespace paint if(sp->alpha_channel) { nana::rectangle s_good_r, d_good_r; - if(overlap(src_r, sp->pixel_size, nana::rectangle(x, y, src_r.width, src_r.height), paint::detail::drawable_size(drawable), s_good_r, d_good_r)) + if(overlap(src_r, sp->pixel_size, nana::rectangle(p_dst.x, p_dst.y, src_r.width, src_r.height), paint::detail::drawable_size(drawable), s_good_r, d_good_r)) { pixel_buffer d_pixbuf; d_pixbuf.attach(drawable, d_good_r); @@ -660,16 +660,16 @@ namespace nana{ namespace paint assign_windows_bitmapinfo(sp->pixel_size, bi); ::SetDIBitsToDevice(drawable->context, - x, y, src_r.width, src_r.height, + p_dst.x, p_dst.y, src_r.width, src_r.height, src_r.x, static_cast(sp->pixel_size.height) - src_r.y - src_r.height, 0, sp->pixel_size.height, sp->raw_pixel_buffer, &bi, DIB_RGB_COLORS); #elif defined(NANA_X11) - sp->put(drawable->pixmap, drawable->context, src_r.x, src_r.y, x, y, src_r.width, src_r.height); + sp->put(drawable->pixmap, drawable->context, src_r.x, src_r.y, p_dst.x, p_dst.y, src_r.width, src_r.height); #endif } } - void pixel_buffer::paste(native_window_type wd, int x, int y) const + void pixel_buffer::paste(native_window_type wd, const point& p_dst) const { auto sp = storage_.get(); if(nullptr == wd || nullptr == sp) return; @@ -681,7 +681,7 @@ namespace nana{ namespace paint assign_windows_bitmapinfo(sp->pixel_size, bi); ::SetDIBitsToDevice(handle, - x, y, sp->pixel_size.width, sp->pixel_size.height, + p_dst.x, p_dst.y, sp->pixel_size.width, sp->pixel_size.height, 0, 0, 0, sp->pixel_size.height, sp->raw_pixel_buffer, &bi, DIB_RGB_COLORS); @@ -690,7 +690,7 @@ namespace nana{ namespace paint #elif defined(NANA_X11) auto & spec = nana::detail::platform_spec::instance(); Display * disp = spec.open_display(); - sp->put(reinterpret_cast(wd), XDefaultGC(disp, XDefaultScreen(disp)), 0, 0, x, y, sp->pixel_size.width, sp->pixel_size.height); + sp->put(reinterpret_cast(wd), XDefaultGC(disp, XDefaultScreen(disp)), 0, 0, p_dst.x, p_dst.y, sp->pixel_size.width, sp->pixel_size.height); #endif } @@ -1039,13 +1039,13 @@ namespace nana{ namespace paint } } - void pixel_buffer::blur(const nana::rectangle& r, std::size_t radius) + void pixel_buffer::blur(const ::nana::rectangle& r, std::size_t radius) { auto sp = storage_.get(); if(nullptr == sp || radius < 1) return; - nana::rectangle good_r; - if(overlap(r, this->size(), good_r)) + ::nana::rectangle good_r; + if (overlap(r, ::nana::rectangle{ this->size() }, good_r)) (*(sp->img_pro.blur))->process(*this, good_r, radius); } }//end namespace paint