diff --git a/.travis.yml b/.travis.yml index 7b71b9ec..82604edf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -44,10 +44,8 @@ matrix: before_install: - cd .. - - git clone --depth=1 --branch=hotfix https://github.com/qPCR4vir/nana-demo.git nana-demo + - git clone --depth=1 --branch=develop https://github.com/qPCR4vir/nana-demo.git nana-demo - export PATH="$HOME/bin:$PATH" - - #- mkdir ~/bin #it seemd that a bin already exists from 20170901 - wget --no-check-certificate --no-clobber -O /tmp/tools/cmake https://cmake.org/files/v3.12/cmake-3.12.0-rc3-Linux-x86_64.sh || true - chmod -R +x /tmp/tools @@ -65,7 +63,7 @@ before_script : - cd demo-build script: - - cmake -G"Unix Makefiles" ../nana-demo -DCMAKE_INSTALL_PREFIX=.. -DNANA_CMAKE_ENABLE_JPEG=ON -DNANA_CMAKE_FIND_BOOST_FILESYSTEM=ON -DNANA_CMAKE_AUTOMATIC_GUI_TESTING=ON + - cmake -G"Unix Makefiles" ../nana-demo -DCMAKE_INSTALL_PREFIX=.. -DNANA_CMAKE_ENABLE_JPEG=ON -DNANA_CMAKE_FIND_BOOST_FILESYSTEM=OFF -DNANA_CMAKE_AUTOMATIC_GUI_TESTING=ON -DNANA_CMAKE_INSTALL=OFF - make install # todo: separate resources from sources (a directory for images) - ls diff --git a/CMakeLists.txt b/CMakeLists.txt index e9252dce..69e1de1d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,7 +22,7 @@ # cmake 3.12 have more better modern c++ support cmake_minimum_required(VERSION 3.12 FATAL_ERROR) -project(nana VERSION 1.6.2 +project(nana VERSION 1.7.1 DESCRIPTION "C++ GUI library" HOMEPAGE_URL http://nanapro.org LANGUAGES CXX ) @@ -30,6 +30,7 @@ project(nana VERSION 1.6.2 ####################### Main setting of Nana targets, sources and installs ##################### add_library(nana) +add_library(nana::nana ALIAS nana) target_compile_features(nana PUBLIC cxx_std_17) # need after cxx_std_14 or cxx_std_17 ?? @@ -54,27 +55,25 @@ target_compile_features(nana # in your own CMakeLists.txt, and them : # target_link_libraries(yourApp PRIVATE nana ) -set(NANA_SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/source) - -set(NANA_SOURCE_SUBDIRS - /. - /detail - /detail/posix - /filesystem - /gui - /gui/detail - /gui/widgets - /gui/widgets/skeletons - /paint - /paint/detail - /system - /threads - ) +set(NANA_SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/source) +set(NANA_SOURCE_SUBDIRS /. + /detail + /detail/posix + /filesystem + /gui + /gui/detail + /gui/widgets + /gui/widgets/skeletons + /paint + /paint/detail + /system + /threads + ) if(NANA_CMAKE_ENABLE_AUDIO) list(APPEND NANA_SOURCE_SUBDIRS - /audio - /audio/detail - ) + /audio + /audio/detail + ) endif() # collect all source files in the source-sub-dir @@ -87,26 +86,24 @@ target_sources(nana PRIVATE ${SOURCES}) ### collect all headers sub-directories in a list to avoid duplication ### # To show .h files in Visual Studio, add them to the list of sources in add_executable / add_library / target_sources # and Use SOURCE_GROUP if all your sources are in the same directory -set(NANA_INCLUDE_DIR ${CMAKE_CURRENT_LIST_DIR}/include) - -set(NANA_INCLUDE_SUBDIRS - /. - /filesystem - /gui - /gui/detail - /gui/widgets - /gui/widgets/skeletons - /paint - /paint/detail - /pat - /system - /threads - ) +set(NANA_INCLUDE_DIR ${CMAKE_CURRENT_LIST_DIR}/include) +set(NANA_INCLUDE_SUBDIRS /. + /filesystem + /gui + /gui/detail + /gui/widgets + /gui/widgets/skeletons + /paint + /paint/detail + /pat + /system + /threads + ) if(NANA_CMAKE_ENABLE_AUDIO) list(APPEND NANA_INCLUDE_SUBDIRS - /audio - /audio/detail - ) + /audio + /audio/detail + ) endif() foreach(subdir ${NANA_INCLUDE_SUBDIRS}) diff --git a/appveyor.yml b/appveyor.yml index 4305ba46..ab87092f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,6 @@ version: 1.0.{build} +image: + - Visual Studio 2017 build: - project: build\vc2015\nana.sln + project: build\vc2017\nana.sln verbosity: minimal \ No newline at end of file diff --git a/build/cmake/compilers.cmake b/build/cmake/compilers.cmake index 8cc53765..c8db4976 100644 --- a/build/cmake/compilers.cmake +++ b/build/cmake/compilers.cmake @@ -14,8 +14,10 @@ if(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") # AND NOT MINGW?? - target_compile_options(nana PRIVATE -Wall - PUBLIC -g ) + target_compile_options(nana PRIVATE -Wall) + + # todo: set in target property of nana + set(CMAKE_CXX_FLAGS_RELEASE "-O3 -march=native -mtune=native -DNDEBUG") set(THREADS_PREFER_PTHREAD_FLAG ON) # todo - test this find_package(Threads REQUIRED) diff --git a/build/cmake/install_nana.cmake b/build/cmake/install_nana.cmake index 2b6851d6..45636484 100644 --- a/build/cmake/install_nana.cmake +++ b/build/cmake/install_nana.cmake @@ -1,8 +1,8 @@ -option(NANA_CMAKE_INSTALL "Install nana when compile the library (to be consumed without cmake)" OFF) +option(NANA_CMAKE_INSTALL "Install nana when compile the library (to be consumed without cmake)" ON) # Install the include directories too. if(NANA_CMAKE_INSTALL) - # this is the prefered method to consume nana directly with some specific bulid system + # This is a method to consume nana with a bulid system not directly supported by nana. # Is your responsability to ensure all compiler options are compatible with the compilation # of the project linking to the nana lib here generated target_sources(nana PRIVATE ${HEADERS}) diff --git a/build/cmake/select_filesystem.cmake b/build/cmake/select_filesystem.cmake index 10c9836f..7b2163de 100644 --- a/build/cmake/select_filesystem.cmake +++ b/build/cmake/select_filesystem.cmake @@ -1,11 +1,12 @@ -# The ISO C++ File System Technical Specification (ISO-TS, or STD) is optional. +# The ISO C++ File System Technical Specification (ISO-TS, or STD) was optional. # http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4100.pdf -# This is not a workaround, but an user option. -# The library maybe available in the std library in use or from Boost (almost compatible) +# It is part of c++17. +# The library may be not available or working correctly in the std library in use. As a workaround we may try +# to "implement" it (ab)using Boost (almost compatible) # http://www.boost.org/doc/libs/1_60_0/libs/filesystem/doc/index.htm # or you can choose to use the (partial, but functional) implementation provided by nana. # If you include the file or -# the selected option will be set by nana into std::experimental::filesystem +# the selected option will be set by nana into std::filesystem # By default Nana will try to use the STD. If STD is not available and NANA_CMAKE_FIND_BOOST_FILESYSTEM # is set to ON nana will try to use boost if available. Nana own implementation will be use if none of # the previus were selected or available. diff --git a/build/cmake/shared_libs.cmake b/build/cmake/shared_libs.cmake index 0734b47a..febb3b42 100644 --- a/build/cmake/shared_libs.cmake +++ b/build/cmake/shared_libs.cmake @@ -40,12 +40,7 @@ if(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") # AN if(BUILD_SHARED_LIBS) target_compile_options(nana PUBLIC -lgcc -lstdc++) else() - - if(MINGW) - target_compile_options(nana PUBLIC -static) # -static ?? cmake knows BUILD_SHARED_LIBS - else() - target_compile_options(nana PUBLIC -static-libgcc -static-libstdc++) - endif() + target_link_libraries(nana PUBLIC -static-libgcc -static-libstdc++) endif(BUILD_SHARED_LIBS) endif() \ No newline at end of file diff --git a/build/cmake/verbose.cmake b/build/cmake/verbose.cmake index 6d95eb9e..a1f9be06 100644 --- a/build/cmake/verbose.cmake +++ b/build/cmake/verbose.cmake @@ -30,6 +30,8 @@ if (NANA_CMAKE_VERBOSE_PREPROCESSOR) cmake_print_variables(CMAKE_BUILD_TYPE) cmake_print_variables(CMAKE_CONFIGURATION_TYPES) + cmake_print_variables(CMAKE_CXX_FLAGS_RELEASE) + message ( "CMAKE_CXX_COMPILER_ID = " ${CMAKE_CXX_COMPILER_ID}) message ( "COMPILER_IS_CLANG = " ${COMPILER_IS_CLANG}) message ( "CMAKE_COMPILER_IS_GNUCXX = " ${CMAKE_COMPILER_IS_GNUCXX}) diff --git a/build/vc2017/nana.vcxproj b/build/vc2017/nana.vcxproj index 7ae3d890..24f5ef80 100644 --- a/build/vc2017/nana.vcxproj +++ b/build/vc2017/nana.vcxproj @@ -102,6 +102,7 @@ WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) MultiThreadedDebug true + stdcpplatest Windows @@ -116,6 +117,7 @@ _DEBUG;_LIB;%(PreprocessorDefinitions) MultiThreadedDebug true + stdcpplatest Windows @@ -132,6 +134,7 @@ WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) MultiThreaded true + stdcpplatest Windows @@ -150,6 +153,7 @@ NDEBUG;_LIB;%(PreprocessorDefinitions) MultiThreaded true + stdcpplatest Windows @@ -252,6 +256,7 @@ + diff --git a/build/vc2017/nana.vcxproj.filters b/build/vc2017/nana.vcxproj.filters index e88f7ed3..4923833d 100644 --- a/build/vc2017/nana.vcxproj.filters +++ b/build/vc2017/nana.vcxproj.filters @@ -474,6 +474,9 @@ Include + + Include\gui + diff --git a/build/vc2019/nana.sln b/build/vc2019/nana.sln new file mode 100644 index 00000000..d01415ee --- /dev/null +++ b/build/vc2019/nana.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26228.4 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nana", "nana.vcxproj", "{42D0520F-EFA5-4831-84FE-2B9085301C5D}" +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 + {42D0520F-EFA5-4831-84FE-2B9085301C5D}.Debug|x64.ActiveCfg = Debug|x64 + {42D0520F-EFA5-4831-84FE-2B9085301C5D}.Debug|x64.Build.0 = Debug|x64 + {42D0520F-EFA5-4831-84FE-2B9085301C5D}.Debug|x86.ActiveCfg = Debug|Win32 + {42D0520F-EFA5-4831-84FE-2B9085301C5D}.Debug|x86.Build.0 = Debug|Win32 + {42D0520F-EFA5-4831-84FE-2B9085301C5D}.Release|x64.ActiveCfg = Release|x64 + {42D0520F-EFA5-4831-84FE-2B9085301C5D}.Release|x64.Build.0 = Release|x64 + {42D0520F-EFA5-4831-84FE-2B9085301C5D}.Release|x86.ActiveCfg = Release|Win32 + {42D0520F-EFA5-4831-84FE-2B9085301C5D}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/build/vc2019/nana.vcxproj b/build/vc2019/nana.vcxproj new file mode 100644 index 00000000..34b0b29d --- /dev/null +++ b/build/vc2019/nana.vcxproj @@ -0,0 +1,316 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {42D0520F-EFA5-4831-84FE-2B9085301C5D} + Win32Proj + nana + + + + StaticLibrary + true + v142 + Unicode + + + StaticLibrary + false + v142 + true + Unicode + + + StaticLibrary + true + v142 + Unicode + + + StaticLibrary + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + ..\..\include;$(IncludePath) + ../bin/ + ..\..\..\temp\$(ProjectName)\$(PlatformToolset)_$(Configuration)_$(PlatformShortName)\ + $(ProjectName)_$(PlatformToolset)_$(Configuration)_$(PlatformShortName) + + + ..\..\include;$(IncludePath) + ../bin/ + ..\..\..\temp\$(ProjectName)\$(PlatformToolset)_$(Configuration)_$(PlatformShortName)\ + $(ProjectName)_$(PlatformToolset)_$(Configuration)_$(PlatformShortName) + + + ../bin/ + ..\..\..\temp\$(ProjectName)\$(PlatformToolset)_$(Configuration)_$(PlatformShortName)\ + $(ProjectName)_$(PlatformToolset)_$(Configuration)_$(PlatformShortName) + ..\..\include;$(IncludePath) + + + ../bin/ + ..\..\..\temp\$(ProjectName)\$(PlatformToolset)_$(Configuration)_$(PlatformShortName)\ + $(ProjectName)_$(PlatformToolset)_$(Configuration)_$(PlatformShortName) + ..\..\include;$(IncludePath) + + + + + + Level3 + Disabled + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + MultiThreadedDebug + true + stdcpplatest + + + Windows + + + + + + + Level3 + Disabled + _DEBUG;_LIB;%(PreprocessorDefinitions) + MultiThreadedDebug + true + stdcpplatest + + + Windows + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + MultiThreaded + true + stdcpplatest + + + Windows + true + true + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_LIB;%(PreprocessorDefinitions) + MultiThreaded + true + stdcpplatest + + + Windows + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/build/vc2019/nana.vcxproj.filters b/build/vc2019/nana.vcxproj.filters new file mode 100644 index 00000000..4923833d --- /dev/null +++ b/build/vc2019/nana.vcxproj.filters @@ -0,0 +1,489 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {81850bad-7436-405a-beb5-357c5e34f039} + + + {44582b36-4575-4663-ac02-e80417f95d05} + + + {b7e3cdb7-99ac-473d-86c8-53dddce70480} + + + {02fa693c-edc1-4e04-bf1d-ec3c2a89182a} + + + {cffe7506-b96c-42aa-a747-41b5115d9580} + + + {b6b2c032-c6a4-4884-8c14-eca4aa69ef0c} + + + {58f2e0f8-4d63-40db-807d-d7adf71c4ebe} + + + {f288a25d-3ce8-4c2e-a86f-9aeda44bc557} + + + {90b2da01-605d-489b-b6c5-2af8d3c2d8a6} + + + {430feed0-e1d9-45cb-8d59-e1a48a04d19f} + + + {dcf62634-a658-453b-a58d-f1a96a12a8b8} + + + {c1cdf46a-519f-422a-947f-39e173045414} + + + {d68bd89c-170f-445f-b79f-aa03c881ab6b} + + + {a5d87649-2cd1-4a8f-a1f9-7151eaf6c772} + + + {0e6a58ab-652c-45d7-b9aa-8d9f2fa80ea1} + + + + + Sources + + + Sources + + + Sources + + + Sources + + + Sources + + + Sources + + + Sources + + + Sources + + + Sources\audio\detail + + + Sources\audio\detail + + + Sources\audio\detail + + + Sources\audio + + + Sources\detail + + + Sources\filesystem + + + Sources\gui\detail + + + Sources\gui\detail + + + Sources\gui\detail + + + Sources\gui\detail + + + Sources\gui\detail + + + Sources\gui\detail + + + Sources\gui\detail + + + Sources\gui\detail + + + Sources\gui\detail + + + Sources\gui\detail + + + Sources\gui\widgets\skeletons + + + Sources\gui\widgets\skeletons + + + Sources\gui\widgets + + + Sources\gui\widgets + + + Sources\gui\widgets + + + Sources\gui\widgets + + + Sources\gui\widgets + + + Sources\gui\widgets + + + Sources\gui\widgets + + + Sources\gui\widgets + + + Sources\gui\widgets + + + Sources\gui\widgets + + + Sources\gui\widgets + + + Sources\gui\widgets + + + Sources\gui\widgets + + + Sources\gui\widgets + + + Sources\gui\widgets + + + Sources\gui\widgets + + + Sources\gui\widgets + + + Sources\gui\widgets + + + Sources\gui\widgets + + + Sources\gui\widgets + + + Sources\gui\widgets + + + Sources\gui\widgets + + + Sources\gui\widgets + + + Sources\paint\detail + + + Sources\paint\detail + + + Sources\paint + + + Sources\paint + + + Sources\paint + + + Sources\paint + + + Sources\paint + + + Sources\system + + + Sources\system + + + Sources\system + + + Sources\system + + + Sources\threads + + + Sources\detail + + + Sources\gui + + + Sources\gui + + + Sources\gui + + + Sources\gui + + + Sources\gui + + + Sources\gui + + + Sources\gui + + + Sources\gui + + + Sources\gui + + + Sources\gui + + + Sources\gui + + + Sources\gui + + + Sources\gui + + + Sources\gui + + + Sources\gui + + + Sources\gui + + + Sources\gui + + + Sources\gui + + + + + Include\gui\widgets + + + Include\gui\widgets + + + Include\gui\widgets + + + Include\gui\widgets + + + Include\gui\widgets + + + Include\gui\widgets + + + Include\gui\widgets + + + Include\gui\widgets + + + Include\gui\widgets + + + Include\gui\widgets + + + Include\gui\widgets + + + Include\gui\widgets + + + Include\gui\widgets + + + Include\gui\widgets + + + Include\gui\widgets + + + Include\gui\widgets + + + Include\gui\widgets + + + Include\gui\widgets + + + Include\gui\widgets + + + Include\gui\widgets + + + Include\gui\widgets + + + Include\gui\widgets + + + Include\gui\widgets + + + Include\gui + + + Include\gui + + + Include\gui + + + Include\gui + + + Include\gui + + + Include\gui + + + Include\gui + + + Include\gui + + + Include\gui + + + Include\gui + + + Include\gui + + + Include\gui + + + Include\gui + + + Include\gui + + + Include\gui + + + Include\gui + + + Include\gui + + + Include + + + Include + + + Include + + + Include + + + Include + + + Include + + + Include + + + Include + + + Include + + + Include + + + Include + + + Include + + + Include + + + Include + + + Include + + + Include + + + Include + + + Include + + + Include + + + Include + + + Include\gui + + + + + Include + + + Include + + + \ No newline at end of file diff --git a/include/nana/deploy.hpp b/include/nana/deploy.hpp index 168358c5..e3091d23 100644 --- a/include/nana/deploy.hpp +++ b/include/nana/deploy.hpp @@ -118,7 +118,5 @@ namespace nana } } -#define NANA_RGB(a) (((DWORD)(a) & 0xFF)<<16) | ((DWORD)(a) & 0xFF00) | (((DWORD)(a) & 0xFF0000) >> 16 ) - #include #endif //NANA_DEPLOY_HPP diff --git a/include/nana/gui.hpp b/include/nana/gui.hpp index 70bcb077..09c2f7b9 100644 --- a/include/nana/gui.hpp +++ b/include/nana/gui.hpp @@ -1 +1,54 @@ -#include "gui/wvl.hpp" \ No newline at end of file +/** + * Nana GUI Header + * Nana C++ Library(http://www.nanapro.org) + * Copyright(C) 2003-2019 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.hpp + * @description + * the header file contains the files required for running of Nana.GUI + */ + +#ifndef NANA_GUI_HPP +#define NANA_GUI_HPP + +#include "gui/compact.hpp" +#include "gui/screen.hpp" +#include "gui/widgets/form.hpp" +#include "gui/drawing.hpp" +#include "gui/msgbox.hpp" +#include "gui/place.hpp" + + +namespace nana +{ +#ifdef NANA_AUTOMATIC_GUI_TESTING + + /// @brief Take control of the GUI and optionally automatically tests it. + /// + /// @detail It transfers to nana the program flow control, which begin pumping messages + /// from the underlying OS, interpreting and sending it with suitable arguments + /// to the nana widgets that registered a response in the corresponding event. + /// It also accept arguments to be used in case of automatic GUI testing. + /// Other Way the arguments are ignored. + void exec( + unsigned wait = 1, ///< for the GUI to be constructed, in seconds + unsigned wait_end = 1, ///< for the GUI to be destructed, in seconds + std::function = {} ///< emit events to mimics user actions and may assert results + ); + + /// send a click message to this widget - useful in GUI testing + void click(widget& w); + + /// in seconds + void Wait(unsigned wait = 0); +#else + void exec(); +#endif + + +}//end namespace nana +#endif diff --git a/include/nana/gui/basis.hpp b/include/nana/gui/basis.hpp index b868a00a..e6ee771c 100644 --- a/include/nana/gui/basis.hpp +++ b/include/nana/gui/basis.hpp @@ -24,10 +24,11 @@ namespace nana { namespace detail { - struct native_window_handle_impl{}; - struct window_handle_impl{}; - struct event_handle_impl{}; - struct native_drawable_impl{}; + struct basic_window; + + struct native_window_handle_impl; + struct native_drawable_impl; + struct event_handle_impl; } struct accel_key @@ -87,10 +88,11 @@ namespace nana struct root_tag : public widget_tag{ static const flags value = flags::root; }; }// end namespace category - using native_window_type = detail::native_window_handle_impl*; - using window = detail::window_handle_impl*; ///< \see [What is window class ](https://sourceforge.net/p/nanapro/discussion/general/thread/bd0fabfb/) - using event_handle = detail::event_handle_impl*; - using native_drawable_type = detail::native_drawable_impl*; + using window = detail::basic_window*; ///< The window handle type representing nana window objects + using native_window_type = detail::native_window_handle_impl*; ///< The native window handle type representing system native windows. E.g, HWND in windows, Window in X11 + + using event_handle = detail::event_handle_impl*; ///< The event handle type representing nana window events + using native_drawable_type = detail::native_drawable_impl*; ///< The drawable handle type representing system native drawable objects. E.g. HDC in windows, Drawable in X11 struct keyboard diff --git a/include/nana/gui/compact.hpp b/include/nana/gui/compact.hpp new file mode 100644 index 00000000..c4a9789a --- /dev/null +++ b/include/nana/gui/compact.hpp @@ -0,0 +1,57 @@ +/** + * Nana GUI Library Definition + * Nana C++ Library(http://www.nanapro.org) + * Copyright(C) 2003-2019 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/compact.hpp + * @description + * the header file contains the files required for running of Nana.GUI + */ + +#ifndef NANA_GUI_WVL_HPP +#define NANA_GUI_WVL_HPP + +#include "programming_interface.hpp" + +namespace nana +{ + namespace detail + { + struct form_loader_private + { + template friend class form_loader; + private: + static void insert_form(::nana::widget*); + }; + + template + class form_loader + { + public: + template + Form & operator()(Args &&... args) const + { + auto p = new Form(std::forward(args)...); + + if (p->empty()) + throw std::runtime_error("form_loader failed to create the form"); + + + detail::form_loader_private::insert_form(p); + if (IsVisible) + p->show(); + + return *p; + } + + }; + } + + template + using form_loader = detail::form_loader; +}//end namespace nana +#endif diff --git a/include/nana/gui/detail/bedrock.hpp b/include/nana/gui/detail/bedrock.hpp index 9dc3812c..834431ac 100644 --- a/include/nana/gui/detail/bedrock.hpp +++ b/include/nana/gui/detail/bedrock.hpp @@ -1,7 +1,7 @@ /** * A Bedrock Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -37,24 +37,36 @@ namespace detail bedrock(const bedrock&) = delete; bedrock& operator=(const bedrock&) = delete; public: - using core_window_t = basic_window; - struct thread_context; class flag_guard; + /// RAII class for window message processing + class root_guard + { + public: + /// Enables lazy_update + root_guard(bedrock& brock, basic_window* root_wd); + + /// Disables lazy-update and clears update requesters queue. + ~root_guard(); + private: + bedrock& brock_; + basic_window* const root_wd_; + }; + ~bedrock(); void pump_event(window, bool is_modal); - void flush_surface(core_window_t*, bool forced, const rectangle* update_area = nullptr); + void flush_surface(basic_window*, bool forced, const rectangle* update_area = nullptr); static int inc_window(thread_t tid = 0); thread_context* open_thread_context(thread_t tid = 0); thread_context* get_thread_context(thread_t tid = 0); void remove_thread_context(thread_t tid = 0); static bedrock& instance(); - core_window_t* focus(); + basic_window* focus(); - void set_menubar_taken(core_window_t*); + void set_menubar_taken(basic_window*); //Delay Restores focus when a menu which attached to menubar is closed void delay_restore(int); @@ -70,7 +82,7 @@ namespace detail bool shortkey_occurred() const; element_store& get_element_store() const; - void map_through_widgets(core_window_t*, native_drawable_type); + void map_through_widgets(basic_window*, native_drawable_type); //Closes the windows which are associated with the specified thread. If the given thread_id is 0, it closes all windows void close_thread_window(thread_t thread_id); @@ -80,28 +92,28 @@ namespace detail static void delete_platform_assoc(window_platform_assoc*); void keyboard_accelerator(native_window_type, const accel_key&, const std::function&); public: - void event_expose(core_window_t *, bool exposed); - void event_move(core_window_t*, int x, int y); - bool event_msleave(core_window_t*); - void event_focus_changed(core_window_t* root_wd, native_window_type receiver, bool getting); - void thread_context_destroy(core_window_t*); + void event_expose(basic_window *, bool exposed); + void event_move(basic_window*, int x, int y); + bool event_msleave(basic_window*); + void event_focus_changed(basic_window* root_wd, native_window_type receiver, bool getting); + void thread_context_destroy(basic_window*); void thread_context_lazy_refresh(); - void update_cursor(core_window_t*); - void set_cursor(core_window_t*, nana::cursor, thread_context*); - void define_state_cursor(core_window_t*, nana::cursor, thread_context*); - void undefine_state_cursor(core_window_t*, thread_context*); + void update_cursor(basic_window*); + void set_cursor(basic_window*, nana::cursor, thread_context*); + void define_state_cursor(basic_window*, nana::cursor, thread_context*); + void undefine_state_cursor(basic_window*, thread_context*); color_schemes& scheme(); events_operation& evt_operation(); window_manager& wd_manager(); - void manage_form_loader(core_window_t*, bool insert_or_remove); + void manage_form_loader(basic_window*, bool insert_or_remove); public: // if 'bForce__EmitInternal', then ONLY internal (widget's) events are processed (even through explicit filtering) - bool emit(event_code, core_window_t*, const event_arg&, bool ask_update, thread_context*, const bool bForce__EmitInternal = false); + bool emit(event_code, basic_window*, const event_arg&, bool ask_update, thread_context*, const bool bForce__EmitInternal = false); private: - void _m_emit_core(event_code, core_window_t*, bool draw_only, const event_arg&, const bool bForce__EmitInternal); - void _m_event_filter(event_code, core_window_t*, thread_context*); + void _m_emit_core(event_code, basic_window*, bool draw_only, const event_arg&, const bool bForce__EmitInternal); + void _m_event_filter(event_code, basic_window*, thread_context*); private: static bedrock bedrock_object; diff --git a/include/nana/gui/detail/effects_renderer.hpp b/include/nana/gui/detail/effects_renderer.hpp deleted file mode 100644 index 3a980caa..00000000 --- a/include/nana/gui/detail/effects_renderer.hpp +++ /dev/null @@ -1,223 +0,0 @@ -/* -* Effects Renderer -* Nana C++ Library(http://www.nanapro.org) -* Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) -* -* Distributed under the Boost Software License, Version 1.0. -* (See accompanying file LICENSE_1_0.txt or copy at -* http://www.boost.org/LICENSE_1_0.txt) -* -* @file: nana/gui/detail/effects_renderer.cpp -*/ - -#ifndef NANA_GUI_DETAIL_EFFECTS_RENDERER_HPP -#define NANA_GUI_DETAIL_EFFECTS_RENDERER_HPP -#include -#include -#include -#include -#include - -namespace nana{ - namespace detail - { - template - class edge_nimbus_renderer - { - edge_nimbus_renderer() = default; - public: - using core_window_t = CoreWindow; - using window_layer = ::nana::detail::window_layout; - using graph_reference = ::nana::paint::graphics&; - - static edge_nimbus_renderer& instance() - { - static edge_nimbus_renderer object; - return object; - } - - constexpr unsigned weight() const - { - return 2; - } - - void erase(core_window_t* wd) - { - 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; - - for (auto i = nimbus.begin(); i != nimbus.end(); ++i) - { - 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; - - nana::rectangle r; - for(auto & action : nimbus) - { - if(_m_edge_nimbus(action.window, focused) && window_layer::read_visual_rectangle(action.window, r)) - { - 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)) || (focused == action.window) || !action.rendered || (action.window->other.upd_state == core_window_t::update_state::refreshed)) - { - rd_set.emplace_back(r, action.window); - action.rendered = true; - } - } - else if(action.rendered) - { - action.rendered = false; - - 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); - } - } - } - - 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); - } - } - - rectangle wd_r{ wd->pos_root, wd->dimension }; - wd_r.pare_off(-static_cast(this->weight())); - //Render - for (auto & rd : rd_set) - { - auto other_wd = rd.second; - - if (other_wd != wd) - { - rectangle other_r{ other_wd->pos_root, other_wd->dimension }; - other_r.pare_off(-static_cast(this->weight())); - if (!overlapped(wd_r, other_r)) - continue; - } - _m_render_edge_nimbus(other_wd, rd.first); - } - } - private: - /// Determines whether the effect will be rendered for the given window. - static bool _m_edge_nimbus(core_window_t * const wd, core_window_t * const focused_wd) - { - // Don't render the effect if the window is disabled. - if (wd->flags.enabled) - { - if ((focused_wd == wd) && (static_cast(wd->effect.edge_nimbus) & static_cast(effects::edge_nimbus::active))) - return true; - else if ((static_cast(wd->effect.edge_nimbus) & static_cast(effects::edge_nimbus::over)) && (wd->flags.action == mouse_action::hovered)) - return true; - } - return false; - } - - void _m_render_edge_nimbus(core_window_t* wd, const nana::rectangle & visual) - { - wd->flags.action_before = wd->flags.action; - - auto r = visual; - r.pare_off(-static_cast(weight())); - 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.right() > visual.right()) || (good_r.bottom() > visual.bottom())) - { - auto graph = wd->root_graph; - nana::paint::pixel_buffer pixbuf(graph->handle(), r); - - pixel_argb_t px0, px1, px2, px3; - - px0 = pixbuf.pixel(0, 0); - px1 = pixbuf.pixel(r.width - 1, 0); - px2 = pixbuf.pixel(0, r.height - 1); - px3 = pixbuf.pixel(r.width - 1, r.height - 1); - - good_r.x = good_r.y = 1; - good_r.width = r.width - 2; - good_r.height = r.height - 2; - pixbuf.rectangle(good_r, wd->annex.scheme->activated.get_color(), 0.95, false); - - good_r.x = good_r.y = 0; - good_r.width = r.width; - good_r.height = r.height; - pixbuf.rectangle(good_r, wd->annex.scheme->activated.get_color(), 0.4, false); - - pixbuf.pixel(0, 0, px0); - pixbuf.pixel(r.width - 1, 0, px1); - pixbuf.pixel(0, r.height - 1, px2); - pixbuf.pixel(r.width - 1, r.height - 1, px3); - - pixbuf.paste(wd->root, { r.x, r.y }); - - std::vector overlaps; - if(window_layer::read_overlaps(wd, visual, overlaps)) - { - for(auto & wdr : overlaps) - graph->paste(wd->root, wdr.r, wdr.r.x, wdr.r.y); - } - } - else - wd->root_graph->paste(wd->root, visual, visual.x, visual.y); - } - } - }; - } -}//end namespace nana - -#endif diff --git a/include/nana/gui/detail/general_events.hpp b/include/nana/gui/detail/general_events.hpp index 2171cb7a..f58f9680 100644 --- a/include/nana/gui/detail/general_events.hpp +++ b/include/nana/gui/detail/general_events.hpp @@ -24,9 +24,13 @@ namespace nana { + namespace API + { + bool is_window(window); ///< Determines whether a window is existing, equal to !empty_window. + } + namespace detail { - bool check_window(window); void events_operation_register(event_handle); class event_interface @@ -36,16 +40,16 @@ namespace nana virtual void remove(event_handle) = 0; }; - class docker_interface + class event_docker_interface { public: - virtual ~docker_interface() = default; + virtual ~event_docker_interface() = default; virtual event_interface* get_event() const = 0; }; struct docker_base - : public docker_interface + : public event_docker_interface { event_interface * const event_ptr; bool flag_deleted; @@ -78,11 +82,11 @@ namespace nana event_base * const evt_; }; - event_handle _m_emplace(detail::docker_interface*, bool in_front); + event_handle _m_emplace(detail::event_docker_interface*, bool in_front); protected: unsigned emitting_count_{ 0 }; bool deleted_flags_{ false }; - std::vector * dockers_{ nullptr }; + std::vector * dockers_{ nullptr }; }; }//end namespace detail @@ -228,7 +232,7 @@ namespace nana d->invoke(arg); - if (window_handle && (!detail::check_window(window_handle))) + if (window_handle && (!::nana::API::is_window(window_handle))) break; } } diff --git a/include/nana/gui/detail/window_layout.hpp b/include/nana/gui/detail/window_layout.hpp index 9d628946..01256ce6 100644 --- a/include/nana/gui/detail/window_layout.hpp +++ b/include/nana/gui/detail/window_layout.hpp @@ -35,11 +35,9 @@ namespace detail class window_layout { public: - typedef basic_window core_window_t; - struct wd_rectangle { - core_window_t * window; + basic_window * window; rectangle r; }; @@ -49,27 +47,27 @@ namespace detail try_refresh }; public: - static void paint(core_window_t*, paint_operation, bool request_refresh_children); + static void paint(basic_window*, paint_operation, bool request_refresh_children); - static bool maproot(core_window_t*, bool have_refreshed, bool request_refresh_children); + static bool maproot(basic_window*, bool have_refreshed, bool request_refresh_children); - static void paste_children_to_graphics(core_window_t*, nana::paint::graphics& graph); + static void paste_children_to_graphics(basic_window*, nana::paint::graphics& graph); //read_visual_rectangle //@brief: Reads the visual rectangle of a window, the visual rectangle's reference frame is to root widget, // the visual rectangle is a rectangular block that a window should be displayed on screen. // The result is a rectangle that is a visible area for its ancesters. - static bool read_visual_rectangle(core_window_t*, nana::rectangle& visual); + static bool read_visual_rectangle(basic_window*, nana::rectangle& visual); //read_overlaps // reads the overlaps that are overlapped a rectangular block - static bool read_overlaps(core_window_t*, const nana::rectangle& vis_rect, std::vector& blocks); + static bool read_overlaps(basic_window*, const nana::rectangle& vis_rect, std::vector& blocks); - static bool enable_effects_bground(core_window_t *, bool enabled); + static bool enable_effects_bground(basic_window *, bool enabled); //make_bground // update the glass buffer of a glass window. - static void make_bground(core_window_t* const); + static void make_bground(basic_window* const); private: /// _m_paste_children @@ -82,16 +80,16 @@ namespace detail * @param graph A graphics object to which the child windows are pasted. * @param graph_rpos The reference point to the graph. */ - static void _m_paste_children(core_window_t* window, bool has_refreshed, bool request_refresh_children, const nana::rectangle& parent_rect, nana::paint::graphics& graph, const nana::point& graph_rpos); + static void _m_paste_children(basic_window* window, bool has_refreshed, bool request_refresh_children, const nana::rectangle& parent_rect, nana::paint::graphics& graph, const nana::point& graph_rpos); - static void _m_paint_glass_window(core_window_t*, bool is_redraw, bool is_child_refreshed, bool called_by_notify, bool notify_other); + static void _m_paint_glass_window(basic_window*, bool is_redraw, bool is_child_refreshed, bool called_by_notify, bool notify_other); //Notify the windows which have brground to update their background buffer. - static void _m_notify_glasses(core_window_t* const sigwd); + static void _m_notify_glasses(basic_window* const sigwd); private: struct data_section { - std::vector effects_bground_windows; + std::vector effects_bground_windows; }; static data_section data_sect; };//end class window_layout diff --git a/include/nana/gui/detail/window_manager.hpp b/include/nana/gui/detail/window_manager.hpp index d9c26326..8c4390d3 100644 --- a/include/nana/gui/detail/window_manager.hpp +++ b/include/nana/gui/detail/window_manager.hpp @@ -67,94 +67,93 @@ namespace detail using native_window = native_window_type; using mutex_type = revertible_mutex; - using core_window_t = basic_window; - window_manager(); ~window_manager(); - std::size_t number_of_core_window() const; + std::size_t window_count() const; mutex_type & internal_lock() const; - void all_handles(std::vector&) const; + void all_handles(std::vector&) const; - void event_filter(core_window_t*, bool is_make, event_code); + void event_filter(basic_window*, bool is_make, event_code); - bool available(core_window_t*); - bool available(core_window_t *, core_window_t*); + bool available(basic_window*); + bool available(basic_window *, basic_window*); - 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*); - void close(core_window_t*); + basic_window* create_root(basic_window*, bool nested, rectangle, const appearance&, widget*); + basic_window* create_widget(basic_window*, const rectangle&, bool is_lite, widget*); + void close(basic_window*); //destroy //@brief: Delete the window handle - void destroy(core_window_t*); + void destroy(basic_window*); //destroy_handle //@brief: Delete window handle, the handle type must be a root and a frame. // Deletes a window whose category type is a root type or a frame type. - void destroy_handle(core_window_t*); + void destroy_handle(basic_window*); - void icon(core_window_t*, const paint::image& small_icon, const paint::image& big_icon); + void icon(basic_window*, const paint::image& small_icon, const paint::image& big_icon); - bool show(core_window_t* wd, bool visible); + bool show(basic_window* wd, bool visible); //find a widget window at specified position //@param root A root window //@param pos Position //@param ignore_captured A flag indicates whether to ignore redirecting the result to its captured window. If this paramter is true, it returns the window at the position, if the parameter is false, it returns the captured window if the captured window don't ignore children. - core_window_t* find_window(native_window_type root, const point& pos, bool ignore_captured = false); + basic_window* find_window(native_window_type root, const point& pos, bool ignore_captured = false); //move the wnd and its all children window, x and y is a relatively coordinate for wnd's parent window - bool move(core_window_t*, int x, int y, bool passive); - bool move(core_window_t*, const rectangle&); + bool move(basic_window*, int x, int y, bool passive); + bool move(basic_window*, const rectangle&); - bool size(core_window_t*, nana::size, bool passive, bool ask_update); + bool size(basic_window*, nana::size, bool passive, bool ask_update); - core_window_t* root(native_window_type) const; + basic_window* root(native_window_type) const; //Copy the root buffer that wnd specified into DeviceContext - void map(core_window_t*, bool forced, const rectangle* update_area = nullptr); + void map(basic_window*, bool forced, const rectangle* update_area = nullptr); - bool update(core_window_t*, bool redraw, bool force, const rectangle* update_area = nullptr); - void refresh_tree(core_window_t*); + bool update(basic_window*, bool redraw, bool force, const rectangle* update_area = nullptr); + void update_requesters(basic_window* root_wd); + void refresh_tree(basic_window*); - void do_lazy_refresh(core_window_t*, bool force_copy_to_screen, bool refresh_tree = false); + void do_lazy_refresh(basic_window*, bool force_copy_to_screen, bool refresh_tree = false); - bool set_parent(core_window_t* wd, core_window_t* new_parent); - core_window_t* set_focus(core_window_t*, bool root_has_been_focused, arg_focus::reason); + bool set_parent(basic_window* wd, basic_window* new_parent); + basic_window* set_focus(basic_window*, bool root_has_been_focused, arg_focus::reason); - core_window_t* capture_redirect(core_window_t*); + basic_window* capture_redirect(basic_window*); bool capture_window_entered(int root_x, int root_y, bool& prev); - core_window_t * capture_window() const; - void capture_window(core_window_t*, bool capture, bool ignore_children_if_captured); + basic_window * capture_window() const; + void capture_window(basic_window*, bool capture, bool ignore_children_if_captured); - void enable_tabstop(core_window_t*); - core_window_t* tabstop(core_window_t*, bool forward) const; //forward means move to next in logic. + void enable_tabstop(basic_window*); + basic_window* tabstop(basic_window*, bool forward) const; //forward means move to next in logic. void remove_trash_handle(thread_t tid); - bool enable_effects_bground(core_window_t*, bool); + bool enable_effects_bground(basic_window*, bool); - bool calc_window_point(core_window_t*, nana::point&); + bool calc_window_point(basic_window*, nana::point&); root_misc* root_runtime(native_window) const; - bool register_shortkey(core_window_t*, unsigned long key); - void unregister_shortkey(core_window_t*, bool with_children); + bool register_shortkey(basic_window*, unsigned long key); + void unregister_shortkey(basic_window*, bool with_children); - core_window_t* find_shortkey(native_window_type, unsigned long key); + basic_window* find_shortkey(native_window_type, unsigned long key); - void set_safe_place(core_window_t* wd, std::function&& fn); + void set_safe_place(basic_window* wd, std::function&& fn); void call_safe_place(thread_t thread_id); private: - 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); - void _m_shortkeys(core_window_t*, bool with_chlidren, std::vector>& keys) const; - core_window_t* _m_find(core_window_t*, const point&); - static bool _m_effective(core_window_t*, const point& root_pos); + void _m_disengage(basic_window*, basic_window* for_new); + void _m_destroy(basic_window*); + void _m_move_core(basic_window*, const point& delta); + void _m_shortkeys(basic_window*, bool with_chlidren, std::vector>& keys) const; + basic_window* _m_find(basic_window*, const point&); + static bool _m_effective(basic_window*, const point& root_pos); private: mutable mutex_type mutex_; @@ -165,10 +164,10 @@ namespace detail { struct captured { - core_window_t *window; + basic_window *window; bool inside; bool ignore_children; - std::vector > history; + std::vector > history; }capture; }attr_; diff --git a/include/nana/gui/dragdrop.hpp b/include/nana/gui/dragdrop.hpp index 1eeb9541..70c91013 100644 --- a/include/nana/gui/dragdrop.hpp +++ b/include/nana/gui/dragdrop.hpp @@ -40,7 +40,16 @@ namespace nana simple_dragdrop(simple_dragdrop&&) = delete; simple_dragdrop& operator=(simple_dragdrop&&) = delete; public: - simple_dragdrop(window source); + explicit simple_dragdrop(window source); + simple_dragdrop(window drag_origin, + std::function when, + window drop_target, + std::function how) + : simple_dragdrop{drag_origin} + { + condition(when); + make_drop(drop_target, how); + } ~simple_dragdrop(); /// Condition checker diff --git a/include/nana/gui/notifier.hpp b/include/nana/gui/notifier.hpp index 31cf28f1..d0d87efa 100644 --- a/include/nana/gui/notifier.hpp +++ b/include/nana/gui/notifier.hpp @@ -59,11 +59,7 @@ namespace nana void text(const ::std::string&); void icon(const ::std::string& icon_file); void insert_icon(const ::std::string& icon_file); -#if 0 //deprecated - void period(unsigned millisecond); -#else void period(std::chrono::milliseconds time); -#endif detail::notifier_events& events(); window handle() const; private: diff --git a/include/nana/gui/place.hpp b/include/nana/gui/place.hpp index a20bd50b..a7a56f17 100644 --- a/include/nana/gui/place.hpp +++ b/include/nana/gui/place.hpp @@ -1,16 +1,16 @@ -/* +/** * An Implementation of Place for Layout * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 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/place.cpp + * @file nana/gui/place.cpp * - * @contributions: - * min/max and splitter bar initial weight by Ariel Vina-Rodriguez. + * @contributions + * error, width/height, min/max and splitter bar initial weight by Ariel Vina-Rodriguez. */ #ifndef NANA_GUI_PLACE_HPP @@ -79,6 +79,7 @@ namespace nana { struct implement; + class field_interface { field_interface(const field_interface&) = delete; @@ -103,6 +104,19 @@ namespace nana virtual void _m_add_agent(const detail::place_agent&) = 0; }; public: + class error :public std::invalid_argument + { + public: + error( const std::string& what, + const place& plc, + std::string field = "unknown", + std::string::size_type pos = std::string::npos); + std::string base_what; + std::string owner_caption; ///< truncate caption (title) of the "placed" widget + std::string div_text; ///< involved div_text + std::string field; ///< posible field where the error ocurred. + std::string::size_type pos; ///< posible position in the div_text where the error ocurred. npos if unknown + }; /// reference to a field manipulator which refers to a field object created by place using field_reference = field_interface &; @@ -119,9 +133,13 @@ namespace nana void splitter_renderer(std::function fn); - void div(std::string div_text); ///< Divides the attached widget into fields. - const std::string& div() const noexcept; ///< Returns div-text that depends on fields status. - void modify(const char* field_name, const char* div_text); ///< Modifies a specified field. + void div(std::string div_text); ///< Divides the attached widget into fields. May throw placa::error + const std::string& div() const noexcept; ///< Returns div-text that depends on fields status. + static bool valid_field_name(const char* name) ///< must begin with _a-zA-Z + { + return name && (*name == '_' || (('a' <= *name && *name <= 'z') || ('A' <= *name && *name <= 'Z'))); + } + void modify(const char* field_name, const char* div_text); ///< Modifies a specified field. May throw placa::error field_reference field(const char* name);///< Returns a field with the specified name. @@ -152,6 +170,8 @@ namespace nana private: implement * impl_; }; + + }//end namespace nana #include diff --git a/include/nana/gui/programming_interface.hpp b/include/nana/gui/programming_interface.hpp index 0b5e42ed..9c463521 100644 --- a/include/nana/gui/programming_interface.hpp +++ b/include/nana/gui/programming_interface.hpp @@ -183,6 +183,13 @@ namespace API }; }//end namespace detail + ///Sets languages + /** + * Specifies the languages in order to make the program display multi-languages correctly + * Under Windows, the pragram can display multi-languages correctly, so this function is useless for Windows. + */ + void font_languages(const std::string& langs); + void exit(); ///< close all windows in current thread void exit_all(); ///< close all windows diff --git a/include/nana/gui/timer.hpp b/include/nana/gui/timer.hpp index 699be103..c360884b 100644 --- a/include/nana/gui/timer.hpp +++ b/include/nana/gui/timer.hpp @@ -22,14 +22,16 @@ namespace nana { - /// Can repeatedly call a piece of code. + + class timer; struct arg_elapse : public event_arg { - long long id; //timer identifier; + timer* sender; //indicates which timer emitted this notification }; + /// Can repeatedly call a piece of code. class timer { struct implement; diff --git a/include/nana/gui/widgets/group.hpp b/include/nana/gui/widgets/group.hpp index 6188ccce..139f7f15 100644 --- a/include/nana/gui/widgets/group.hpp +++ b/include/nana/gui/widgets/group.hpp @@ -35,7 +35,10 @@ namespace nana{ }//end namespace drawerbase class group - : public widget_object + : public widget_object { struct implement; public: @@ -56,9 +59,8 @@ namespace nana{ group(window parent, const rectangle& = {}, bool visible = true); /// The construction that creates the widget and set the title or caption - - group(window parent, ///< a handle to the parent - ::std::string title, ///< caption of the group + group(window parent, ///< a handle to the parent + ::std::string title, ///< caption of the group bool formatted = false, ///< Enable/disable the formatted text for the title unsigned gap = 2, ///< between the content and the external limit const rectangle& r = {} , @@ -98,6 +100,10 @@ namespace nana{ group& enable_format_caption(bool format); group& collocate() noexcept; + + /// this will set the `usr_div_str` for an internal field, called the "user field". + /// The "full" `place` of a `group` widget is internally divided into a field for the title, + /// a field for the added "options" and a field for "user" widgets. group& div(const char* div_str) noexcept; field_reference operator[](const char* field); diff --git a/include/nana/gui/widgets/listbox.hpp b/include/nana/gui/widgets/listbox.hpp index 1d9cdec0..4827a227 100644 --- a/include/nana/gui/widgets/listbox.hpp +++ b/include/nana/gui/widgets/listbox.hpp @@ -1219,7 +1219,6 @@ namespace nana unsigned min_column_width{ 20 }; ///< def=20 . non counting suspension_width - unsigned suspension_width{ 8 }; ///< def= . the trigger will set this to the width if ("...") unsigned text_margin{ 5 }; ///< def= 5. Additional or extended with added (before) to the text width to determine the cell width. cell_w = text_w + ext_w +1 unsigned item_height_ex{ 6 }; ///< Set !=0 !!!! def=6. item_height = text_height + item_height_ex @@ -1292,7 +1291,10 @@ the nana::detail::basic_window member pointer scheme \example listbox_Resolver.cpp */ class listbox - : public widget_object, + : public widget_object, public concepts::any_objective { public: @@ -1462,7 +1464,7 @@ the nana::detail::basic_window member pointer scheme size_type column_size() const; /// Move column to view_position - void move_column(size_type abs_pos, size_type view_pos); + void move_column(size_type abs_pos, size_type view_pos); /// Sort columns in range first_col to last_col inclusive using the values from a row void reorder_columns(size_type first_col, @@ -1568,7 +1570,7 @@ the nana::detail::basic_window member pointer scheme * @param img_collapsed An icon displayed in front of category title when the category is collapsed. * @return the reference of *this. */ - listbox& category_icon(const paint::image& img_expanded, const paint::image&& img_collapsed); + listbox& category_icon(const paint::image& img_expanded, const paint::image& img_collapsed); /// Returns first visible element /** @@ -1590,6 +1592,18 @@ the nana::detail::basic_window member pointer scheme * @return index_pairs containing all visible items. */ index_pairs visibles() const; + + /// Sets a predicate that indicates whether to deselect items when mouse_up is triggered. + /** + * The predicate is called before the listbox attempts to deselect the selected items in the mouse_up event. Other situations, + * the predicates isn't called, for example, releasing mouse button after user performed a box selection, because listbox doesn't deselect the items during this operation. + * @param predicate Decides to deselect the items. + * The paramater of predicate indicates the mouse button which is releasing. + * It returns true to deselect the selected items. It returns false to cancel to deselect the selected items. + */ + void set_deselect(std::function predicate); + + unsigned suspension_width() const; private: drawerbase::listbox::essence & _m_ess() const; nana::any* _m_anyobj(size_type cat, size_type index, bool allocate_if_empty) const override; diff --git a/include/nana/gui/widgets/spinbox.hpp b/include/nana/gui/widgets/spinbox.hpp index d1f779e0..2f489f1d 100644 --- a/include/nana/gui/widgets/spinbox.hpp +++ b/include/nana/gui/widgets/spinbox.hpp @@ -77,7 +77,10 @@ namespace nana /// Spinbox Widget class spinbox - : public widget_object + : public widget_object { public: /// Constructs a spinbox. diff --git a/include/nana/gui/widgets/textbox.hpp b/include/nana/gui/widgets/textbox.hpp index 68c408b7..8557c68a 100644 --- a/include/nana/gui/widgets/textbox.hpp +++ b/include/nana/gui/widgets/textbox.hpp @@ -1,4 +1,4 @@ -/** +/** * A Textbox Implementation * Nana C++ Library(http://www.nanapro.org) * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) @@ -177,6 +177,10 @@ namespace nana /// Read the text from a specified line with a set offset. It returns true for success. bool getline(std::size_t line_index,std::size_t offset,std::string& text) const; + // Get all text from textbox. + // It returns a empty string if failed or the textbox is empty. + std::string text() const { return caption(); } + /// Read the text from a specified line; returns an empty optional on failure std::optional getline(std::size_t pos) const; diff --git a/include/nana/gui/widgets/toolbar.hpp b/include/nana/gui/widgets/toolbar.hpp index 02e88c6c..ed3456e4 100644 --- a/include/nana/gui/widgets/toolbar.hpp +++ b/include/nana/gui/widgets/toolbar.hpp @@ -33,14 +33,55 @@ namespace nana { namespace toolbar { - struct item_proxy - { - nana::toolbar& widget; - std::size_t button; + enum class tool_type + { + button, + toggle + }; - void enable(bool enable_state); + class item_proxy + { + public: + item_proxy(::nana::toolbar*, std::size_t pos); + + bool enable() const; + item_proxy& enable(bool enable_state); + + item_proxy& tooltype(tool_type type); ///< Sets the tool style. + + bool istoggle() const; ///< Returns true if the tool style is toggle. + bool toggle() const; ///< Gets the tool toggle state (only if tool style is toggle). + item_proxy& toggle(bool toggle_state); ///< Sets the tool toggle state (only if tool style is toggle). + std::string toggle_group() const; ///< Returns the toggle group associated with the tool (only if tool style is toggle). + item_proxy& toggle_group(const ::std::string& group); ///< Adds the tool to a toggle group (only if tool style is toggle). + + item_proxy& textout(bool show); ///< Show/Hide the text inside the button + + private: + nana::toolbar* const tb_; + std::size_t const pos_; }; + struct item_type + { + std::string text; + nana::paint::image image; + unsigned pixels{ 0 }; + unsigned position{ 0 }; // last item position. + nana::size textsize; + bool enable{ true }; + + tool_type type{ tool_type::button }; + bool toggle{ false }; + std::string group; + + bool textout{ false }; + + item_type(const std::string& text, const nana::paint::image& img, tool_type type) + :text(text), image(img), type(type) + {} + }; + struct toolbar_events : public general_events { @@ -49,7 +90,6 @@ namespace nana basic_event leave; ///< The mouse leaves a control button. }; - struct item_type; class item_container; class drawer @@ -90,6 +130,7 @@ namespace nana { public: using size_type = std::size_t; ///< A type to count the number of elements. + using tool_type = drawerbase::toolbar::tool_type; toolbar() = default; toolbar(window, bool visible, bool detached=false); @@ -102,6 +143,17 @@ namespace nana bool enable(size_type index) const; void enable(size_type index, bool enable_state); + + void tooltype(size_type index, tool_type type); ///< Sets the tool style. + + bool istoggle(size_type index) const; ///< Returns true if the tool style is toggle. + bool toggle(size_type index) const; ///< Gets the tool toggle state (only if tool style is toggle). + void toggle(size_type index, bool toggle_state); ///< Sets the tool toggle state (only if tool style is toggle). + std::string toggle_group(size_type index) const; ///< Returns the toggle group associated with the tool (only if tool style is toggle). + void toggle_group(size_type index, const ::std::string& group); ///< Adds the tool to a toggle group (only if tool style is toggle). + + void textout(size_type index, bool show); ///< Show/Hide the text inside the button + void scale(unsigned s); ///< Sets the scale of control button. /// Enable to place buttons at right part. After calling it, every new button is right aligned. diff --git a/include/nana/gui/widgets/treebox.hpp b/include/nana/gui/widgets/treebox.hpp index fd48b022..c1edbadf 100644 --- a/include/nana/gui/widgets/treebox.hpp +++ b/include/nana/gui/widgets/treebox.hpp @@ -350,7 +350,9 @@ namespace nana /// \brief Displays a hierarchical list of items, such as the files and directories on a disk. /// See also in [documentation](http://nanapro.org/en-us/documentation/widgets/treebox.htm) class treebox - :public widget_object + :public widget_object { public: /// A type refers to the item and is also used to iterate through the nodes. diff --git a/include/nana/gui/wvl.hpp b/include/nana/gui/wvl.hpp deleted file mode 100644 index c18be253..00000000 --- a/include/nana/gui/wvl.hpp +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Nana GUI Library Definition - * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) - * - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at - * http://www.boost.org/LICENSE_1_0.txt) - * - * @file nana/gui/wvl.hpp - * @description - * the header file contains the files required for running of Nana.GUI - */ - -#ifndef NANA_GUI_WVL_HPP -#define NANA_GUI_WVL_HPP - -#include "programming_interface.hpp" -#include "screen.hpp" -#include "widgets/form.hpp" -#include "drawing.hpp" -#include "msgbox.hpp" -#include "place.hpp" - - -namespace nana -{ - namespace detail - { - struct form_loader_private - { - template friend class form_loader; - private: - static void insert_form(::nana::widget*); - }; - - template - class form_loader - { - public: - template - Form & operator()(Args &&... args) const - { - auto p = new Form(std::forward(args)...); - - if (p->empty()) - throw std::logic_error("form_loader failed to create the form"); - - detail::form_loader_private::insert_form(p); - if (IsVisible) - p->show(); - - return *p; - } - - }; - } - - template - using form_loader = detail::form_loader; - -#ifdef NANA_AUTOMATIC_GUI_TESTING - - /// @brief Take control of the GUI and optionally automatically tests it. - /// - /// @detail It transfers to nana the program flow control, which begin pumping messages - /// from the underlying OS, interpreting and sending it with suitable arguments - /// to the nana widgets that registered a response in the corresponding event. - /// It also accept arguments to be used in case of automatic GUI testing. - /// Other Way the arguments are ignored. - void exec( - unsigned wait = 1, ///< for the GUI to be constructed, in seconds - unsigned wait_end = 1, ///< for the GUI to be destructed, in seconds - std::function = {} ///< emit events to mimics user actions and may assert results - ); - - /// send a click message to this widget - useful in GUI testing - void click(widget& w); - - /// in seconds - void Wait(unsigned wait = 0); -#else - void exec(); -#endif - - -}//end namespace nana -#endif diff --git a/include/nana/paint/text_renderer.hpp b/include/nana/paint/text_renderer.hpp index 96fbbfdb..3510bb3d 100644 --- a/include/nana/paint/text_renderer.hpp +++ b/include/nana/paint/text_renderer.hpp @@ -10,14 +10,20 @@ namespace nana { public: using graph_reference = graphics &; + + enum class mode + { + truncate_with_ellipsis, + truncate_letter_with_ellipsis, + word_wrap + }; text_renderer(graph_reference graph, align = align::left); - nana::size extent_size(int x, int y, const wchar_t*, std::size_t len, unsigned restricted_pixels) const; + nana::size extent_size(int x, int y, const wchar_t*, std::size_t len, unsigned space_pixels) const; void render(const point&, const wchar_t*, std::size_t len); - void render(const point&, const wchar_t*, std::size_t len, unsigned restricted_pixels, bool omitted); - void render(const point&, const wchar_t*, std::size_t len, unsigned restricted_pixels); + void render(const point&, const wchar_t*, std::size_t len, unsigned space_pixels, mode); private: graph_reference graph_; align text_align_; diff --git a/source/datetime.cpp b/source/datetime.cpp index 8b024735..e6e8fa5b 100644 --- a/source/datetime.cpp +++ b/source/datetime.cpp @@ -14,29 +14,28 @@ #if defined(NANA_WINDOWS) #include #endif -#include #include namespace { std::tm localtime() { #if defined(NANA_WINDOWS) && !defined(NANA_MINGW) - time_t t; - ::time(&t); + std::time_t t = std::time(nullptr); std::tm tm; - if(localtime_s(&tm, &t) != 0) - assert(false); + if (localtime_s(&tm, &t) != 0) + throw std::runtime_error("invalid local time"); return tm; #else time_t t = std::time(nullptr); struct tm * tm_addr = std::localtime(&t); - assert(tm_addr); - + if(nullptr == tm_addr) + throw std::runtime_error("invalid local time"); + return *tm_addr; #endif } - + ::nana::date::value to_dateval(const std::tm& t) { return {static_cast(t.tm_year + 1900), static_cast(t.tm_mon + 1), static_cast(t.tm_mday)}; @@ -51,7 +50,7 @@ namespace { namespace nana { - //class date + //class date void date::set(const std::tm& t) { value_ = to_dateval(t); diff --git a/source/detail/mswin/platform_spec.hpp b/source/detail/mswin/platform_spec.hpp index 1a44ce8b..ae83562d 100644 --- a/source/detail/mswin/platform_spec.hpp +++ b/source/detail/mswin/platform_spec.hpp @@ -1,7 +1,7 @@ /** * Platform Specification Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -96,19 +96,19 @@ namespace detail unsigned whitespace_pixels; }string; + unsigned fgcolor_rgb{ 0xFFFFFFFF }; + unsigned bgcolor_rgb{ 0xFFFFFFFF }; + unsigned fgcolor_native{ 0xFFFFFFFF }; //Windows RGB format: 0xBBGGRR + unsigned bgcolor_native{ 0xFFFFFFFF }; //Windows RGB format + drawable_impl_type(const drawable_impl_type&) = delete; drawable_impl_type& operator=(const drawable_impl_type&) = delete; drawable_impl_type(); ~drawable_impl_type(); - unsigned get_color() const; - unsigned get_text_color() const; void set_color(const ::nana::color&); void set_text_color(const ::nana::color&); - private: - unsigned color_{ 0xffffffff }; - unsigned text_color_{0xffffffff}; }; class platform_spec diff --git a/source/detail/platform_abstraction.cpp b/source/detail/platform_abstraction.cpp index 515f6ace..ea6032b2 100644 --- a/source/detail/platform_abstraction.cpp +++ b/source/detail/platform_abstraction.cpp @@ -1,4 +1,5 @@ #include "platform_abstraction.hpp" +#include #include #include "../paint/truetype.hpp" @@ -153,6 +154,389 @@ IsWindows8OrGreater() namespace nana { +#ifdef NANA_USE_XFT + //A fallback fontset provides the multiple languages support. + class fallback_fontset + { + public: + fallback_fontset(): + disp_(::nana::detail::platform_spec::instance().open_display()) + { + } + + ~fallback_fontset() + { + for(auto xft: xftset_) + ::XftFontClose(disp_, xft); + } + + void open(const std::string& font_desc, const std::set& langs) + { + for(auto xft: xftset_) + ::XftFontClose(disp_, xft); + + xftset_.clear(); + + std::set loaded; + for(auto & lang : langs) + { + std::string patstr = "*" + font_desc + ":lang=" + lang; + + auto pat = ::XftNameParse(patstr.c_str()); + XftResult res; + auto match_pat = ::XftFontMatch(disp_, ::XDefaultScreen(disp_), pat, &res); + + if (match_pat) + { + char * sf; + if(XftResultTypeMismatch != ::XftPatternGetString(match_pat, "family", 0, &sf)) + { + //Avoid loading a some font repeatedly + if(loaded.count(sf)) + continue; + } + + auto xft = ::XftFontOpenPattern(disp_, match_pat); + if(xft) + xftset_.push_back(xft); + } + } + } + + int draw(::XftDraw* xftdraw, ::XftColor * xftcolor, ::XftFont* xft, int x, int y, const wchar_t* str, std::size_t len) + { + if(nullptr == str || 0 == len) + return 0; + + int const init_x = x; + std::unique_ptr glyph_indexes(new FT_UInt[len]); + + while(true) + { + auto preferred = _m_scan_fonts(xft, str, len, glyph_indexes.get()); + x += _m_draw(xftdraw, xftcolor, preferred.first, x, y, str, preferred.second, glyph_indexes.get()); + + if(len == preferred.second) + break; + + len -= preferred.second; + str += preferred.second; + } + + return x - init_x; + } + + std::unique_ptr glyph_pixels(::XftFont* xft, const wchar_t* str, std::size_t len) + { + if(nullptr == xft || nullptr == str || 0 == len) + return {}; + + std::unique_ptr glyph_indexes{new FT_UInt[len]}; + + std::unique_ptr pxbuf{new unsigned[len]}; + + auto pbuf = pxbuf.get(); + auto pstr = str; + auto size = len; + + while(true) + { + auto preferred = _m_scan_fonts(xft, pstr, size, glyph_indexes.get()); + + _m_glyph_px(preferred.first, pstr, preferred.second, glyph_indexes.get(), pbuf); + + if(size == preferred.second) + break; + + size -= preferred.second; + pstr += preferred.second; + pbuf += preferred.second; + } + + return pxbuf; + } + + nana::size extents(::XftFont* xft, const wchar_t* str, std::size_t len) + { + nana::size extent; + + if(nullptr == str || 0 == len) + return extent; + + std::unique_ptr glyph_indexes(new FT_UInt[len]); + + while(len > 0) + { + auto preferred = _m_scan_fonts(xft, str, len, glyph_indexes.get()); + + extent.width += _m_extents(preferred.first, str, preferred.second, glyph_indexes.get()); + + if(preferred.first->ascent + preferred.first->descent > static_cast(extent.height)) + extent.height = preferred.first->ascent + preferred.first->descent; + + len -= preferred.second; + str += preferred.second; + } + return extent; + } + private: + //Tab is a invisible character + int _m_draw(::XftDraw* xftdraw, ::XftColor* xftcolor, ::XftFont* xft, int x, int y, const wchar_t* str, std::size_t len, const FT_UInt* glyph_indexes) + { + int const init_x = x; + + auto p = str; + auto const end = str + len; + + y += xft->ascent; + + ::XGlyphInfo ext; + while(p < end) + { + auto off = p - str; + auto ptab = _m_find_tab(p, end); + if(ptab == p) + { + ++p; + //x += static_cast(tab_pixels_); + continue; + } + + auto const size = ptab - p; + ::XftDrawGlyphs(xftdraw, xftcolor, xft, x, y, glyph_indexes + off, size); + ::XftGlyphExtents(disp_, xft, glyph_indexes + off, size, &ext); + + x += ext.xOff; + + if(ptab == end) + break; + + p = ptab + 1; + } + + return x - init_x; + } + + //Tab is a invisible character + unsigned _m_extents(::XftFont* xft, const wchar_t* const str, const std::size_t len, const FT_UInt* glyph_indexes) + { + unsigned pixels = 0; + auto p = str; + auto const end = str + len; + + ::XGlyphInfo ext; + while(p < end) + { + auto off = p - str; + auto ptab = _m_find_tab(p, end); + if(ptab == p) + { + ++p; + //extents->xOff += tab_pixels_; + continue; + } + + ::XftGlyphExtents(disp_, xft, glyph_indexes + off, ptab - p, &ext); + + pixels += ext.xOff; + + if(end == ptab) + break; + p = ptab + 1; + } + + return pixels; + } + + //Tab is a invisible character + void _m_glyph_px(::XftFont* xft, const wchar_t* str, std::size_t len, const FT_UInt* glyph_indexes, unsigned* pxbuf) + { + auto const end = str + len; + + ::XGlyphInfo extent; + for(auto p = str; p < end; ++p) + { + if('\t' != *p) + { + ::XftGlyphExtents(disp_, xft, glyph_indexes, 1, &extent); + *pxbuf = extent.xOff; + } + else + *pxbuf = 0;//tab_pixels_; + + ++glyph_indexes; + } + } + + static const wchar_t* _m_find_tab(const wchar_t* begin, const wchar_t* end) + { + while(begin < end) + { + if('\t' == *begin) + return begin; + + ++begin; + } + return end; + } + + std::pair<::XftFont*, std::size_t> _m_scan_fonts(::XftFont* xft, const wchar_t* str, std::size_t len, FT_UInt* const glyphs) const + { + auto preferred = xft; + auto idx = ::XftCharIndex(disp_, xft, *str); + if(0 == idx) + { + for(auto ft : xftset_) + { + idx = ::XftCharIndex(disp_, ft, *str); + if(idx) + { + preferred = ft; + break; + } + } + } + + *glyphs = idx; + + if(0 == idx) + { + //scan the str with all fonts until a char index is found. + for(std::size_t i = 1; i < len; ++i) + { + if(::XftCharIndex(disp_, xft, str[i])) + return {preferred, i}; + + for(auto ft : xftset_) + { + if(::XftCharIndex(disp_, ft, str[i])) + return {preferred, i}; + } + glyphs[i] = 0; + } + + return {preferred, len}; + } + + //scan the str with preferred font until a char index is invalid. + for(std::size_t i = 1; i < len; ++i) + { + idx = ::XftCharIndex(disp_, preferred, str[i]); + if(0 == idx) + return {preferred, i}; + + glyphs[i] = idx; + } + + return {preferred, len}; + } + private: + Display* const disp_; + std::vector<::XftFont*> xftset_; + }; + + /// Fallback fontset manager + class fallback_manager + { + public: + fallback_manager(): + langs_(_m_split_lang("ar,hi,zh-cn,zh-tw,ja,ko,th")) + { + } + + void languages(const std::string& lang) + { + langs_ = _m_split_lang(lang); + + for(auto & xft : xft_table_) + { + xft.second->open(xft.first, langs_); + } + } + + std::shared_ptr make_fallback(const std::string& font_desc) + { + auto i = xft_table_.find(font_desc); + if(i != xft_table_.end()) + return i->second; + + auto fb = std::make_shared(); + + fb->open(font_desc, langs_); + + xft_table_[font_desc] = fb; + + return fb; + } + + void release_fallback(std::shared_ptr& p) + { + for(auto i = xft_table_.cbegin(); i != xft_table_.cend(); ++i) + { + if(i->second == p) + { + if(p.use_count() <= 2) + xft_table_.erase(i); + break; + } + } + } + + private: + static std::set _m_split_lang(const std::string& lang) + { + std::set langs; + std::size_t start_pos = 0; + while(true) + { + auto pos = lang.find(',', start_pos); + auto l = lang.substr(start_pos, lang.npos == pos? lang.npos : pos - start_pos); + + if(!l.empty()) + langs.insert(l); + + if(lang.npos == pos) + break; + + start_pos = pos + 1; + } + + return langs; + } + private: + std::set langs_; + std::map> xft_table_; + }; +#endif + + struct platform_runtime + { + std::shared_ptr font; + +#ifdef NANA_X11 + std::map fontconfig_counts; +#endif +#ifdef NANA_USE_XFT + fallback_manager fb_manager; +#endif + }; + + namespace + { + namespace data + { + static platform_runtime* storage; + } + } + + static platform_runtime& platform_storage() + { + if (nullptr == data::storage) + throw std::runtime_error("platform_abstraction is empty"); + + return *data::storage; + } + class internal_font : public font_interface @@ -160,13 +544,24 @@ namespace nana public: using path_type = std::filesystem::path; +#ifdef NANA_USE_XFT + internal_font(const path_type& ttf, const std::string& font_family, double font_size, const font_style& fs, native_font_type native_font, std::shared_ptr fallback): + ttf_(ttf), + family_(font_family), + size_(font_size), + style_(fs), + native_handle_(native_font), + fallback_(fallback) + {} +#else internal_font(const path_type& ttf, const std::string& font_family, double font_size, const font_style& fs, native_font_type native_font): ttf_(ttf), family_(font_family), size_(font_size), style_(fs), native_handle_(native_font) - {} + {} +#endif ~internal_font() { @@ -175,6 +570,7 @@ namespace nana #elif defined(NANA_X11) auto disp = ::nana::detail::platform_spec::instance().open_display(); # ifdef NANA_USE_XFT + platform_storage().fb_manager.release_fallback(fallback_); ::XftFontClose(disp, reinterpret_cast(native_handle_)); # else ::XFreeFontSet(disp, reinterpret_cast(native_handle_)); @@ -203,39 +599,58 @@ namespace nana { return native_handle_; } + +#ifdef NANA_USE_XFT + fallback_fontset* fallback() const + { + return fallback_.get(); + } +#endif private: path_type const ttf_; std::string const family_; double const size_; font_style const style_; native_font_type const native_handle_; - }; - - struct platform_runtime - { - std::shared_ptr font; - -#ifdef NANA_X11 - std::map fontconfig_counts; +#ifdef NANA_USE_XFT + std::shared_ptr fallback_; #endif }; - namespace +#ifdef NANA_USE_XFT + void nana_xft_draw_string(::XftDraw* xftdraw, ::XftColor* xftcolor, font_interface* ft, const nana::point& pos, const wchar_t * str, std::size_t len) { - namespace data - { - static platform_runtime* storage; - } + auto fallback = static_cast(ft)->fallback(); + if(nullptr == fallback) + return; + + auto xft = reinterpret_cast(static_cast(ft)->native_handle()); + fallback->draw(xftdraw, xftcolor, xft, pos.x, pos.y, str, len); } - static platform_runtime& platform_storage() - { - if (nullptr == data::storage) - throw std::runtime_error("platform_abstraction is empty"); - return *data::storage; + nana::size nana_xft_extents(font_interface* ft, const wchar_t* str, std::size_t len) + { + auto fallback = static_cast(ft)->fallback(); + if(nullptr == fallback) + return {}; + + auto xft = reinterpret_cast(static_cast(ft)->native_handle()); + return fallback->extents(xft, str, len); } + std::unique_ptr nana_xft_glyph_pixels(font_interface* ft, const wchar_t* str, std::size_t len) + { + auto fallback = static_cast(ft)->fallback(); + if(nullptr == fallback) + return {}; + + auto xft = reinterpret_cast(static_cast(ft)->native_handle()); + return fallback->glyph_pixels(xft, str, len); + } +#endif + + void platform_abstraction::initialize() { if (nullptr == data::storage) @@ -284,6 +699,13 @@ namespace nana #endif } + void platform_abstraction::font_languages(const std::string& langs) + { +#ifdef NANA_USE_XFT + platform_storage().fb_manager.languages(langs); +#endif + } + ::std::shared_ptr platform_abstraction::default_font(const ::std::shared_ptr& new_font) { auto & r = platform_storage(); @@ -364,10 +786,22 @@ namespace nana auto disp = ::nana::detail::platform_spec::instance().open_display(); # ifdef NANA_USE_XFT if(font_family.empty()) - font_family = '*'; + font_family = "*"; - std::string pat_str = font_family + '-' + std::to_string(size_pt ? size_pt : platform_abstraction::font_default_pt()); - auto pat = ::XftNameParse(pat_str.c_str()); + std::string pat_str = '-' + std::to_string(size_pt ? size_pt : platform_abstraction::font_default_pt()); + if(fs.weight < 400) + pat_str += ":light"; + else if(400 == fs.weight) + pat_str += ":medium"; + else if(fs.weight < 700) + pat_str += ":demibold"; + else + pat_str += (700 == fs.weight ? ":bold": ":black"); + + if(fs.italic) + pat_str += ":slant=italic"; + + auto pat = ::XftNameParse((font_family + pat_str).c_str()); XftResult res; auto match_pat = ::XftFontMatch(disp, ::XDefaultScreen(disp), pat, &res); @@ -387,8 +821,16 @@ namespace nana XFontSet fd = ::XCreateFontSet(display_, const_cast(pat_str.c_str()), &missing_list, &missing_count, &defstr); # endif #endif + if (fd) + { +#ifdef NANA_USE_XFT + auto fallback = platform_storage().fb_manager.make_fallback(pat_str); + return std::make_shared(std::move(ttf), std::move(font_family), size_pt, fs, reinterpret_cast(fd), fallback); +#else return std::make_shared(std::move(ttf), std::move(font_family), size_pt, fs, reinterpret_cast(fd)); +#endif + } return{}; } diff --git a/source/detail/platform_abstraction.hpp b/source/detail/platform_abstraction.hpp index 3e22112a..6af2d170 100644 --- a/source/detail/platform_abstraction.hpp +++ b/source/detail/platform_abstraction.hpp @@ -1,7 +1,7 @@ /* * Platform Abstraction * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2017 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2017-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -34,6 +34,7 @@ namespace nana /// Shutdown before destruction of platform_spec static void shutdown(); static double font_default_pt(); + static void font_languages(const std::string&); static ::std::shared_ptr default_font(const ::std::shared_ptr&); static ::std::shared_ptr make_font(const ::std::string& font_family, double size_pt, const font::font_style& fs); static ::std::shared_ptr make_font_from_ttf(const path_type& ttf, double size_pt, const font::font_style& fs); diff --git a/source/detail/platform_spec_posix.cpp b/source/detail/platform_spec_posix.cpp index f7cbc9d6..8944468b 100644 --- a/source/detail/platform_spec_posix.cpp +++ b/source/detail/platform_spec_posix.cpp @@ -1,7 +1,7 @@ /* * Platform Specification Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Nana Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -35,6 +34,7 @@ #include #include "posix/msg_dispatcher.hpp" +#include "../gui/detail/basic_window.hpp" namespace nana { @@ -95,7 +95,7 @@ namespace detail return std::string(); } //end class conf - +#if 0 //class charset_conv charset_conv::charset_conv(const char* tocode, const char* fromcode) { @@ -140,6 +140,7 @@ namespace detail return rstr; } //end class charset_conv +#endif #endif //Caret implementation @@ -209,15 +210,15 @@ namespace detail class timer_runner { - typedef void (*timer_proc_t)(std::size_t id); + using handler_type = void(*)(const timer_core*); struct timer_tag { - std::size_t id; - thread_t tid; + const timer_core* handle; + thread_t thread_id; std::size_t interval; std::size_t timestamp; - timer_proc_t proc; + handler_type handler; }; //timer_group @@ -233,32 +234,32 @@ namespace detail struct timer_group { bool proc_entered{false}; //This flag indicates whether the timers are going to do event. - std::set timers; - std::vector delay_deleted; + std::set timers; + std::vector delay_deleted; }; public: timer_runner() : is_proc_handling_(false) {} - void set(std::size_t id, std::size_t interval, timer_proc_t proc) + void set(const timer_core* handle, std::size_t interval, handler_type handler) { - auto i = holder_.find(id); + auto i = holder_.find(handle); if(i != holder_.end()) { i->second.interval = interval; - i->second.proc = proc; + i->second.handler = handler; return; } auto tid = nana::system::this_thread_id(); - threadmap_[tid].timers.insert(id); + threadmap_[tid].timers.insert(handle); - timer_tag & tag = holder_[id]; - tag.id = id; - tag.tid = tid; + timer_tag & tag = holder_[handle]; + tag.handle = handle; + tag.thread_id = tid; tag.interval = interval; tag.timestamp = 0; - tag.proc = proc; + tag.handler = handler; } bool is_proc_handling() const @@ -266,12 +267,12 @@ namespace detail return is_proc_handling_; } - void kill(std::size_t id) + bool kill(const timer_core* handle) { - auto i = holder_.find(id); + auto i = holder_.find(handle); if(i != holder_.end()) { - auto tid = i->second.tid; + auto tid = i->second.thread_id; auto ig = threadmap_.find(tid); if(ig != threadmap_.end()) //Generally, the ig should not be the end of threadmap_ @@ -279,20 +280,16 @@ namespace detail auto & group = ig->second; if(!group.proc_entered) { - group.timers.erase(id); + group.timers.erase(handle); if(group.timers.empty()) threadmap_.erase(ig); } else - group.delay_deleted.push_back(id); + group.delay_deleted.push_back(handle); } holder_.erase(i); } - } - - bool empty() const - { - return (holder_.empty()); + return holder_.empty(); } void timer_proc(thread_t tid) @@ -314,7 +311,7 @@ namespace detail tag.timestamp = ticks; try { - tag.proc(tag.id); + tag.handler(tag.handle); }catch(...){} //nothrow } } @@ -330,7 +327,7 @@ namespace detail private: bool is_proc_handling_; std::map threadmap_; - std::map holder_; + std::map holder_; }; drawable_impl_type::drawable_impl_type() @@ -338,49 +335,28 @@ namespace detail string.tab_length = 4; string.tab_pixels = 0; string.whitespace_pixels = 0; -#if defined(NANA_USE_XFT) - conv_.handle = ::iconv_open("UTF-8", NANA_UNICODE); - conv_.code = NANA_UNICODE; -#endif - } - - drawable_impl_type::~drawable_impl_type() - { -#if defined(NANA_USE_XFT) - ::iconv_close(conv_.handle); -#endif - } - - unsigned drawable_impl_type::get_color() const - { - return color_; - } - - unsigned drawable_impl_type::get_text_color() const - { - return text_color_; } void drawable_impl_type::set_color(const ::nana::color& clr) { - color_ = (clr.px_color().value & 0xFFFFFF); + bgcolor_rgb = (clr.px_color().value & 0xFFFFFF); } void drawable_impl_type::set_text_color(const ::nana::color& clr) { - text_color_ = (clr.px_color().value & 0xFFFFFF); + fgcolor_rgb = (clr.px_color().value & 0xFFFFFF); update_text_color(); } void drawable_impl_type::update_color() { - if (color_ != current_color_) + if (bgcolor_rgb != current_color_) { auto & spec = nana::detail::platform_spec::instance(); platform_scope_guard lock; - current_color_ = color_; - auto col = color_; + current_color_ = bgcolor_rgb; + auto col = bgcolor_rgb; switch (spec.screen_depth()) { case 16: @@ -391,18 +367,32 @@ namespace detail } ::XSetForeground(spec.open_display(), context, col); ::XSetBackground(spec.open_display(), context, col); + +#if defined(NANA_USE_XFT) + //xft_fgcolor also needs to be assigned. + //assumes the xft_fgcolor is not assigned in update_color. There is a situation that causes a bug. + // + //update_text_color ( if fgcolor_rgb = A, then current_color = A and xft_fgcolor = A) + //update_color (if bgcolor_rgb = B, then current_color = B and xft_fgcolor is still A) + //update_text_color ( if fgcolor_rgb = B, then current_color = B, xft_fgcolor is still A) + + xft_fgcolor.color.red = ((0xFF0000 & col) >> 16) * 0x101; + xft_fgcolor.color.green = ((0xFF00 & col) >> 8) * 0x101; + xft_fgcolor.color.blue = (0xFF & col) * 0x101; + xft_fgcolor.color.alpha = 0xFFFF; +#endif } } void drawable_impl_type::update_text_color() { - if (text_color_ != current_color_) + if (fgcolor_rgb != current_color_) { auto & spec = nana::detail::platform_spec::instance(); platform_scope_guard lock; - current_color_ = text_color_; - auto col = text_color_; + current_color_ = fgcolor_rgb; + auto col = fgcolor_rgb; switch (spec.screen_depth()) { case 16: @@ -445,7 +435,7 @@ namespace detail } platform_spec::timer_runner_tag::timer_runner_tag() - : runner(0), delete_declared(false) + : runner(nullptr), delete_declared(false) {} platform_spec::platform_spec() @@ -987,30 +977,32 @@ namespace detail return r; } - void platform_spec::set_timer(std::size_t id, std::size_t interval, void (*timer_proc)(std::size_t)) + void platform_spec::set_timer(const timer_core* handle, std::size_t interval, void (*timer_proc)(const timer_core*)) { std::lock_guard lock(timer_.mutex); - if(0 == timer_.runner) + if(!timer_.runner) timer_.runner = new timer_runner; - timer_.runner->set(id, interval, timer_proc); + + timer_.runner->set(handle, interval, timer_proc); timer_.delete_declared = false; } - void platform_spec::kill_timer(std::size_t id) + void platform_spec::kill_timer(const timer_core* handle) { - if(timer_.runner == 0) return; - std::lock_guard lock(timer_.mutex); - timer_.runner->kill(id); - if(timer_.runner->empty()) + if(timer_.runner) { - if(timer_.runner->is_proc_handling() == false) + // Test if there is not a timer after killing + if(timer_.runner->kill(handle)) { - delete timer_.runner; - timer_.runner = 0; + if(timer_.runner->is_proc_handling() == false) + { + delete timer_.runner; + timer_.runner = nullptr; + } + else + timer_.delete_declared = true; } - else - timer_.delete_declared = true; } } @@ -1023,7 +1015,7 @@ namespace detail if(timer_.delete_declared) { delete timer_.runner; - timer_.runner = 0; + timer_.runner = nullptr; timer_.delete_declared = false; } } diff --git a/source/detail/platform_spec_windows.cpp b/source/detail/platform_spec_windows.cpp index 812c2ed0..0fc7d8bc 100644 --- a/source/detail/platform_spec_windows.cpp +++ b/source/detail/platform_spec_windows.cpp @@ -1,7 +1,7 @@ /** * Platform Specification Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -42,28 +42,22 @@ namespace detail ::DeleteObject(pixmap); } - unsigned drawable_impl_type::get_color() const - { - return color_; - } - - unsigned drawable_impl_type::get_text_color() const - { - return text_color_; - } +#define NANA_WINDOWS_RGB(a) (((DWORD)(a) & 0xFF)<<16) | ((DWORD)(a) & 0xFF00) | (((DWORD)(a) & 0xFF0000) >> 16 ) void drawable_impl_type::set_color(const ::nana::color& clr) { - color_ = (clr.px_color().value & 0xFFFFFF); + bgcolor_rgb = (clr.px_color().value & 0xFFFFFF); + bgcolor_native = NANA_WINDOWS_RGB(bgcolor_rgb); } void drawable_impl_type::set_text_color(const ::nana::color& clr) { auto rgb = (clr.px_color().value & 0xFFFFFF); - if (text_color_ != rgb) + if (fgcolor_rgb != rgb) { - ::SetTextColor(context, NANA_RGB(rgb)); - text_color_ = rgb; + fgcolor_rgb = rgb; + fgcolor_native = NANA_WINDOWS_RGB(rgb); + ::SetTextColor(context, fgcolor_native); } } diff --git a/source/detail/posix/platform_spec.hpp b/source/detail/posix/platform_spec.hpp index 84c1c24e..ff37717e 100644 --- a/source/detail/posix/platform_spec.hpp +++ b/source/detail/posix/platform_spec.hpp @@ -1,7 +1,7 @@ /* * Platform Specification Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -42,7 +42,6 @@ #if defined(NANA_USE_XFT) #include - #include #include #endif @@ -62,19 +61,6 @@ namespace detail private: std::ifstream ifs_; }; - - class charset_conv - { - charset_conv(const charset_conv&) = delete; - charset_conv& operator=(const charset_conv*) = delete; - public: - charset_conv(const char* tocode, const char* fromcode); - ~charset_conv(); - std::string charset(const std::string& str) const; - std::string charset(const char * buf, std::size_t len) const; - private: - iconv_t handle_; - }; #endif struct drawable_impl_type @@ -95,16 +81,15 @@ namespace detail unsigned whitespace_pixels; }string; + unsigned fgcolor_rgb{ 0xFFFFFFFF }; + unsigned bgcolor_rgb{ 0xFFFFFFFF }; + #if defined(NANA_USE_XFT) XftDraw * xftdraw{nullptr}; XftColor xft_fgcolor; - const std::string charset(const std::wstring& str, const std::string& strcode); #endif drawable_impl_type(); - ~drawable_impl_type(); - unsigned get_color() const; - unsigned get_text_color() const; void set_color(const ::nana::color&); void set_text_color(const ::nana::color&); @@ -115,16 +100,6 @@ namespace detail drawable_impl_type& operator=(const drawable_impl_type&) = delete; unsigned current_color_{ 0xFFFFFF }; - unsigned color_{ 0xFFFFFFFF }; - unsigned text_color_{ 0xFFFFFFFF }; - -#if defined(NANA_USE_XFT) - struct conv_tag - { - iconv_t handle; - std::string code; - }conv_; -#endif }; struct atombase_tag @@ -171,6 +146,13 @@ namespace detail //A forward declaration of caret data struct caret_rep; + /// class timer_core + /** + * Platform-spec only provides the declaration for intrducing a handle type, the definition + * of timer_core is given by gui/timer.cpp + */ + class timer_core; + class timer_runner; class platform_scope_guard @@ -251,8 +233,8 @@ namespace detail //when native_interface::show a window that is registered as a grab //window, the native_interface grabs the window. Window grab(Window); - void set_timer(std::size_t id, std::size_t interval, void (*timer_proc)(std::size_t id)); - void kill_timer(std::size_t id); + void set_timer(const timer_core*, std::size_t interval, void (*timer_proc)(const timer_core* tm)); + void kill_timer(const timer_core*); void timer_proc(thread_t tid); //Message dispatcher diff --git a/source/filesystem/filesystem.cpp b/source/filesystem/filesystem.cpp index 8be0cce9..9180f084 100644 --- a/source/filesystem/filesystem.cpp +++ b/source/filesystem/filesystem.cpp @@ -110,42 +110,6 @@ namespace nana return tm.str(); } return {}; - -/* - // Deprecated - //Windows stores file times using the FILETIME structure, which is a 64 bit value of 100ns intervals from January 1, 1601. - //What's worse is that this 1601 date is fairly common to see given that it's the all zeroes value, and it is far before the - //earliest date representable with time_t.std::filesystem can't change the reality of the underlying platform. - - - try { -#if NANA_USING_BOOST_FILESYSTEM - // The return type of boost::filesystem::last_write_time isn't - // the same as in nana and std implementations of this function - auto ftime = std::chrono::system_clock::from_time_t(fs::last_write_time(path)); -#else - auto ftime = fs::last_write_time(path); -#endif - - // crash: VS2015 will not read the time for some files (for example: C:/hiberfil.sys) - // and will return file_time_type(-1) without throwing - // https://msdn.microsoft.com/en-us/library/dn823784.aspx - - if (ftime == ((fs::file_time_type::min)())) return{}; - - //A workaround for VC2013 - using time_point = decltype(ftime); - auto cftime = time_point::clock::to_time_t(ftime); - - std::stringstream tm; - tm << std::put_time(std::localtime(&cftime), "%Y-%m-%d, %H:%M:%S"); - return tm.str(); - } - catch (...) { - return{}; - } -#endif -*/ } bool modified_file_time(const fs::path& p, struct tm& t) diff --git a/source/gui/detail/basic_window.cpp b/source/gui/detail/basic_window.cpp index 661e3619..bb585f49 100644 --- a/source/gui/detail/basic_window.cpp +++ b/source/gui/detail/basic_window.cpp @@ -10,7 +10,7 @@ * @file: nana/gui/detail/basic_window.cpp */ -#include +#include "basic_window.hpp" #include namespace nana diff --git a/include/nana/gui/detail/basic_window.hpp b/source/gui/detail/basic_window.hpp similarity index 96% rename from include/nana/gui/detail/basic_window.hpp rename to source/gui/detail/basic_window.hpp index bc0a116f..3d53eab5 100644 --- a/include/nana/gui/detail/basic_window.hpp +++ b/source/gui/detail/basic_window.hpp @@ -14,11 +14,11 @@ #ifndef NANA_GUI_DETAIL_BASIC_WINDOW_HPP #define NANA_GUI_DETAIL_BASIC_WINDOW_HPP #include -#include "drawer.hpp" -#include "events_holder.hpp" -#include "widget_geometrics.hpp" -#include "widget_content_measurer_interface.hpp" -#include "widget_notifier_interface.hpp" +#include +#include +#include +#include +#include #include #include #include diff --git a/source/gui/detail/bedrock_pi.cpp b/source/gui/detail/bedrock_pi.cpp index 0cd1e796..5f31b3d2 100644 --- a/source/gui/detail/bedrock_pi.cpp +++ b/source/gui/detail/bedrock_pi.cpp @@ -11,16 +11,18 @@ */ #include "../../detail/platform_spec_selector.hpp" +#include "basic_window.hpp" #include "bedrock_types.hpp" +#include +#include #include #include -#include #include -#include -#include #include #include #include + +#include #include namespace nana @@ -64,11 +66,6 @@ namespace nana namespace detail { - bool check_window(window wd) - { - return bedrock::instance().wd_manager().available(reinterpret_cast(wd)); - } - void events_operation_register(event_handle evt) { bedrock::instance().evt_operation().register_evt(evt); @@ -77,7 +74,7 @@ namespace nana class bedrock::flag_guard { public: - flag_guard(bedrock* brock, core_window_t * wd) + flag_guard(bedrock* brock, basic_window * wd) : brock_{ brock }, wd_(wd) { wd_->flags.refreshing = true; @@ -90,11 +87,28 @@ namespace nana } private: bedrock *const brock_; - core_window_t *const wd_; - + basic_window *const wd_; }; - bedrock::core_window_t* bedrock::focus() + //class root_guard + bedrock::root_guard::root_guard(bedrock& brock, basic_window* root_wd): + brock_(brock), + root_wd_(root_wd) + { + root_wd_->other.attribute.root->lazy_update = true; + } + + bedrock::root_guard::~root_guard() + { + if (!brock_.wd_manager().available(root_wd_)) + return; + + root_wd_->other.attribute.root->lazy_update = false; + root_wd_->other.attribute.root->update_requesters.clear(); + } + //end class root_guard + + basic_window* bedrock::focus() { auto wd = wd_manager().root(native_interface::get_focus_window()); return (wd ? wd->other.attribute.root->focus : nullptr); @@ -110,7 +124,7 @@ namespace nana return pi_data_->wd_manager; } - void bedrock::manage_form_loader(core_window_t* wd, bool insert_or_remove) + void bedrock::manage_form_loader(basic_window* wd, bool insert_or_remove) { if (insert_or_remove) { @@ -127,7 +141,7 @@ namespace nana void bedrock::close_thread_window(thread_t thread_id) { - std::vector v; + std::vector v; wd_manager().all_handles(v); std::vector roots; @@ -146,7 +160,7 @@ namespace nana native_interface::close_window(i); } - void bedrock::event_expose(core_window_t * wd, bool exposed) + void bedrock::event_expose(basic_window * wd, bool exposed) { if (nullptr == wd) return; @@ -154,11 +168,11 @@ namespace nana arg_expose arg; arg.exposed = exposed; - arg.window_handle = reinterpret_cast(wd); + arg.window_handle = wd; if (emit(event_code::expose, wd, arg, false, get_thread_context())) { //Get the window who has the activated caret - const core_window_t * caret_wd = ((wd->annex.caret_ptr && wd->annex.caret_ptr->activated()) ? wd : wd->child_caret()); + auto const caret_wd = ((wd->annex.caret_ptr && wd->annex.caret_ptr->activated()) ? wd : wd->child_caret()); if (caret_wd) { if (exposed) @@ -181,19 +195,19 @@ namespace nana } } - void bedrock::event_move(core_window_t* wd, int x, int y) + void bedrock::event_move(basic_window* wd, int x, int y) { if (wd) { arg_move arg; - arg.window_handle = reinterpret_cast(wd); + arg.window_handle = wd; arg.x = x; arg.y = y; emit(event_code::move, wd, arg, true, get_thread_context()); } } - bool bedrock::event_msleave(core_window_t* hovered) + bool bedrock::event_msleave(basic_window* hovered) { if (wd_manager().available(hovered) && hovered->flags.enabled) { @@ -201,7 +215,7 @@ namespace nana arg_mouse arg; arg.evt_code = event_code::mouse_leave; - arg.window_handle = reinterpret_cast(hovered); + arg.window_handle = hovered; arg.pos.x = arg.pos.y = 0; arg.left_button = arg.right_button = arg.mid_button = false; arg.ctrl = arg.shift = false; @@ -212,12 +226,12 @@ namespace nana } //The wd must be a root window - void bedrock::event_focus_changed(core_window_t* root_wd, native_window_type receiver, bool getting) + void bedrock::event_focus_changed(basic_window* root_wd, native_window_type receiver, bool getting) { auto focused = root_wd->other.attribute.root->focus; arg_focus arg; - arg.window_handle = reinterpret_cast(focused); + arg.window_handle = focused; arg.getting = getting; arg.receiver = receiver; @@ -243,7 +257,7 @@ namespace nana } } - void bedrock::update_cursor(core_window_t * wd) + void bedrock::update_cursor(basic_window * wd) { internal_scope_guard isg; if (wd_manager().available(wd)) @@ -265,7 +279,7 @@ namespace nana } } - void bedrock::set_menubar_taken(core_window_t* wd) + void bedrock::set_menubar_taken(basic_window* wd) { auto pre = pi_data_->menu.taken_window; pi_data_->menu.taken_window = wd; @@ -379,7 +393,7 @@ namespace nana return pi_data_->scheme; } - void bedrock::_m_emit_core(event_code evt_code, core_window_t* wd, bool draw_only, const ::nana::event_arg& event_arg, const bool bForce__EmitInternal) + void bedrock::_m_emit_core(event_code evt_code, basic_window* wd, bool draw_only, const ::nana::event_arg& event_arg, const bool bForce__EmitInternal) { auto retain = wd->annex.events_ptr; auto evts_ptr = retain.get(); @@ -399,7 +413,7 @@ namespace nana wd->drawer.click(*arg, bForce__EmitInternal); } if (bProcess__External_event) - evts_ptr->click.emit(*arg, reinterpret_cast(wd)); + evts_ptr->click.emit(*arg, wd); } } break; @@ -454,7 +468,7 @@ namespace nana } if (bProcess__External_event) - evt_addr->emit(*arg, reinterpret_cast(wd)); + evt_addr->emit(*arg, wd); break; } case event_code::mouse_wheel: @@ -469,7 +483,7 @@ namespace nana } if (bProcess__External_event) - evts_ptr->mouse_wheel.emit(*arg, reinterpret_cast(wd)); + evts_ptr->mouse_wheel.emit(*arg, wd); } break; } @@ -513,7 +527,7 @@ namespace nana } if (bProcess__External_event) - evt_addr->emit(*arg, reinterpret_cast(wd)); + evt_addr->emit(*arg, wd); break; } case event_code::expose: @@ -521,7 +535,7 @@ namespace nana { auto arg = dynamic_cast(&event_arg); if (arg) - evts_ptr->expose.emit(*arg, reinterpret_cast(wd)); + evts_ptr->expose.emit(*arg, wd); } break; case event_code::focus: @@ -535,7 +549,7 @@ namespace nana wd->drawer.focus(*arg, bForce__EmitInternal); } if (bProcess__External_event) - evts_ptr->focus.emit(*arg, reinterpret_cast(wd)); + evts_ptr->focus.emit(*arg, wd); } break; } @@ -550,7 +564,7 @@ namespace nana wd->drawer.move(*arg, bForce__EmitInternal); } if (bProcess__External_event) - evts_ptr->move.emit(*arg, reinterpret_cast(wd)); + evts_ptr->move.emit(*arg, wd); } break; } @@ -565,7 +579,7 @@ namespace nana wd->drawer.resizing(*arg, bForce__EmitInternal); } if (bProcess__External_event) - evts_ptr->resizing.emit(*arg, reinterpret_cast(wd)); + evts_ptr->resizing.emit(*arg, wd); } break; } @@ -580,7 +594,7 @@ namespace nana wd->drawer.resized(*arg, bForce__EmitInternal); } if (bProcess__External_event) - evts_ptr->resized.emit(*arg, reinterpret_cast(wd)); + evts_ptr->resized.emit(*arg, wd); } break; } @@ -592,7 +606,7 @@ namespace nana { auto evt_root = dynamic_cast(evts_ptr); if (evt_root) - evt_root->unload.emit(*arg, reinterpret_cast(wd)); + evt_root->unload.emit(*arg, wd); } } break; @@ -601,7 +615,7 @@ namespace nana { auto arg = dynamic_cast(&event_arg); if (arg) - evts_ptr->destroy.emit(*arg, reinterpret_cast(wd)); + evts_ptr->destroy.emit(*arg, wd); } break; default: @@ -609,7 +623,7 @@ namespace nana } } - void bedrock::thread_context_destroy(core_window_t * wd) + void bedrock::thread_context_destroy(basic_window * wd) { auto ctx = get_thread_context(0); if(ctx && ctx->event_window == wd) @@ -620,15 +634,15 @@ namespace nana { auto ctx = get_thread_context(0); if(ctx && ctx->event_window) - ctx->event_window->other.upd_state = core_window_t::update_state::refreshed; + ctx->event_window->other.upd_state = basic_window::update_state::refreshed; } - bool bedrock::emit(event_code evt_code, core_window_t* wd, const ::nana::event_arg& arg, bool ask_update, thread_context* thrd, const bool bForce__EmitInternal) + bool bedrock::emit(event_code evt_code, basic_window* wd, const ::nana::event_arg& arg, bool ask_update, thread_context* thrd, const bool bForce__EmitInternal) { if(wd_manager().available(wd) == false) return false; - core_window_t * prev_wd = nullptr; + basic_window * prev_wd = nullptr; if(thrd) { prev_wd = thrd->event_window; @@ -663,7 +677,7 @@ namespace nana return good_wd; } - void bedrock::_m_event_filter(event_code event_id, core_window_t * wd, thread_context * thrd) + void bedrock::_m_event_filter(event_code event_id, basic_window * wd, thread_context * thrd) { auto not_state_cur = (wd->root_widget->other.attribute.root->state_cursor == nana::cursor::arrow); diff --git a/source/gui/detail/bedrock_posix.cpp b/source/gui/detail/bedrock_posix.cpp index 9c96a7cd..b3ed3fb4 100644 --- a/source/gui/detail/bedrock_posix.cpp +++ b/source/gui/detail/bedrock_posix.cpp @@ -141,9 +141,9 @@ namespace detail delete impl_; } - void bedrock::flush_surface(core_window_t* wd, bool forced, const rectangle* update_area) + void bedrock::flush_surface(basic_window* wd, bool forced, const rectangle* update_area) { - wd->drawer.map(reinterpret_cast(wd), forced, update_area); + wd->drawer.map(wd, forced, update_area); } //inc_window @@ -248,14 +248,14 @@ namespace detail return impl_->estore; } - void bedrock::map_through_widgets(core_window_t*, native_drawable_type) + void bedrock::map_through_widgets(basic_window*, native_drawable_type) { //No implementation for Linux } void assign_arg(arg_mouse& arg, basic_window* wd, unsigned msg, const XEvent& evt) { - arg.window_handle = reinterpret_cast(wd); + arg.window_handle = wd; arg.button = ::nana::mouse::any_button; int mask_state = 0; @@ -308,7 +308,7 @@ namespace detail void assign_arg(arg_focus& arg, basic_window* wd, native_window_type recv, bool getting) { - arg.window_handle = reinterpret_cast(wd); + arg.window_handle = wd; arg.receiver = recv; arg.getting = getting; arg.focus_reason = arg_focus::reason::general; @@ -317,7 +317,7 @@ namespace detail void assign_arg(arg_wheel& arg, basic_window* wd, const XEvent& evt) { arg.evt_code = event_code::mouse_wheel; - arg.window_handle = reinterpret_cast(wd); + arg.window_handle = wd; if (ButtonRelease == evt.type && (evt.xbutton.button == Button4 || evt.xbutton.button == Button5)) { arg.evt_code = event_code::mouse_wheel; @@ -371,12 +371,12 @@ namespace detail if(msgwd) { arg_dropfiles arg; - arg.window_handle = reinterpret_cast(msgwd); + arg.window_handle = msgwd; arg.files.swap(*msg.u.mouse_drop.files); delete msg.u.mouse_drop.files; arg.pos.x = msg.u.mouse_drop.x - msgwd->pos_root.x; arg.pos.y = msg.u.mouse_drop.y - msgwd->pos_root.y; - msgwd->annex.events_ptr->mouse_dropfiles.emit(arg, reinterpret_cast(msgwd)); + msgwd->annex.events_ptr->mouse_dropfiles.emit(arg, msgwd); brock.wd_manager().do_lazy_refresh(msgwd, false); } break; @@ -512,11 +512,9 @@ namespace detail auto const native_window = rruntime->window->root; - - nana::detail::charset_conv charset(NANA_UNICODE, "UTF-8"); - const std::string& str = charset.charset(std::string(keybuf, keybuf + keybuf_len)); - auto const charbuf = reinterpret_cast(str.c_str()); - auto const len = str.size() / sizeof(wchar_t); + auto wstr = nana::to_wstring(std::string{keybuf, keybuf + keybuf_len}); + auto const charbuf = wstr.c_str(); + auto const len = wstr.length(); for(std::size_t i = 0; i < len; ++i) { @@ -539,15 +537,15 @@ namespace detail auto shr_wd = wd_manager.find_shortkey(native_window, arg.key); if(shr_wd) { - arg.window_handle = reinterpret_cast(shr_wd); + arg.window_handle = shr_wd; brock.emit(event_code::shortkey, shr_wd, arg, true, &context); } continue; } arg.evt_code = event_code::key_char; - arg.window_handle = reinterpret_cast(msgwd); - msgwd->annex.events_ptr->key_char.emit(arg, reinterpret_cast(msgwd)); + arg.window_handle = msgwd; + msgwd->annex.events_ptr->key_char.emit(arg, msgwd); if(arg.ignore == false && wd_manager.available(msgwd)) draw_invoker(&drawer::key_char, msgwd, arg, &context); } @@ -556,34 +554,11 @@ namespace detail context.is_alt_pressed = false; } - class window_proc_guard - { - public: - window_proc_guard(detail::basic_window* wd) : - root_wd_(wd) - { - root_wd_->other.attribute.root->lazy_update = true; - } - - ~window_proc_guard() - { - if (!bedrock::instance().wd_manager().available(root_wd_)) - return; - - root_wd_->other.attribute.root->lazy_update = false; - root_wd_->other.attribute.root->update_requesters.clear(); - } - private: - detail::basic_window* const root_wd_; - }; - void window_proc_for_xevent(Display* /*display*/, XEvent& xevent) { - typedef detail::bedrock::core_window_t core_window_t; - static auto& brock = detail::bedrock::instance(); static unsigned long last_mouse_down_time; - static core_window_t* last_mouse_down_window; + static basic_window* last_mouse_down_window; auto native_window = reinterpret_cast(event_window(xevent)); auto & wd_manager = brock.wd_manager(); @@ -593,7 +568,8 @@ namespace detail { auto const root_wd = root_runtime->window; auto msgwnd = root_wd; - window_proc_guard wp_guard{ root_wd }; + + detail::bedrock::root_guard rw_guard{ brock, root_wd }; auto& context = *brock.get_thread_context(msgwnd->thread_id); @@ -786,7 +762,7 @@ namespace detail { msgwnd->set_action(mouse_action::hovered); - click_arg.window_handle = reinterpret_cast(msgwnd); + click_arg.window_handle = msgwnd; draw_invoker(&drawer::click, msgwnd, click_arg, &context); } } @@ -804,16 +780,16 @@ namespace detail draw_invoker(&drawer::mouse_up, msgwnd, arg, &context); if(click_arg.window_handle) - evt_ptr->click.emit(click_arg, reinterpret_cast(msgwnd)); + evt_ptr->click.emit(click_arg, msgwnd); if (wd_manager.available(msgwnd)) { arg.evt_code = event_code::mouse_up; - evt_ptr->mouse_up.emit(arg, reinterpret_cast(msgwnd)); + evt_ptr->mouse_up.emit(arg, msgwnd); } } else if(click_arg.window_handle) - msgwnd->annex.events_ptr->click.emit(click_arg, reinterpret_cast(msgwnd)); + msgwnd->annex.events_ptr->click.emit(click_arg, msgwnd); wd_manager.do_lazy_refresh(msgwnd, false); } @@ -937,7 +913,7 @@ namespace detail //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); + msgwnd->drawer.map(msgwnd, true, &update_area); } } break; @@ -1016,7 +992,7 @@ namespace detail else if((keyboard::space == os_code) && msgwnd->flags.space_click_enabled) { //Clicked by spacebar - if((nullptr == pressed_wd) && (nullptr == pressed_wd_space)) + if((nullptr == pressed_wd) && (nullptr == pressed_wd_space) && msgwnd->flags.enabled) { arg_mouse arg; arg.alt = modifiers_status.alt; @@ -1027,7 +1003,7 @@ namespace detail arg.mid_button = false; arg.pos.x = 0; arg.pos.y = 0; - arg.window_handle = reinterpret_cast(msgwnd); + arg.window_handle = msgwnd; msgwnd->set_action(mouse_action::pressed); @@ -1049,7 +1025,7 @@ namespace detail bool focused = (brock.focus() == msgwnd); arg_keyboard arg; arg.evt_code = event_code::key_press; - arg.window_handle = reinterpret_cast(msgwnd); + arg.window_handle = msgwnd; arg.ignore = false; arg.key = os_code; brock.get_key_state(arg); @@ -1071,7 +1047,7 @@ namespace detail arg.ignore = false; arg.key = os_code; arg.evt_code = event_code::key_press; - arg.window_handle = reinterpret_cast(msgwnd); + arg.window_handle = msgwnd; brock.emit(event_code::key_press, msgwnd, arg, true, &context); @@ -1119,7 +1095,7 @@ namespace detail msgwnd = brock.focus(); if(msgwnd) { - if(msgwnd == pressed_wd_space) + if((msgwnd == pressed_wd_space) && msgwnd->flags.enabled) { msgwnd->set_action(mouse_action::normal); @@ -1127,7 +1103,7 @@ namespace detail arg_click click_arg; click_arg.mouse_args = nullptr; - click_arg.window_handle = reinterpret_cast(msgwnd); + click_arg.window_handle = msgwnd; arg_mouse arg; arg.alt = false; @@ -1138,7 +1114,7 @@ namespace detail arg.mid_button = false; arg.pos.x = 0; arg.pos.y = 0; - arg.window_handle = reinterpret_cast(msgwnd); + arg.window_handle = msgwnd; draw_invoker(&drawer::mouse_up, msgwnd, arg, &context); @@ -1152,7 +1128,7 @@ namespace detail arg_keyboard arg; arg.evt_code = event_code::key_release; - arg.window_handle = reinterpret_cast(msgwnd); + arg.window_handle = msgwnd; arg.ignore = false; arg.key = os_code; brock.get_key_state(arg); @@ -1178,7 +1154,7 @@ namespace detail arg_keyboard arg; arg.evt_code = event_code::key_release; - arg.window_handle = reinterpret_cast(msgwnd); + arg.window_handle = msgwnd; arg.ignore = false; arg.key = os_code; brock.get_key_state(arg); @@ -1205,7 +1181,7 @@ namespace detail if(msgwnd->flags.enabled && (atoms.wm_delete_window == static_cast(xclient.data.l[0]))) { arg_unload arg; - arg.window_handle = reinterpret_cast(msgwnd); + arg.window_handle = msgwnd; arg.cancel = false; brock.emit(event_code::unload, msgwnd, arg, true, &context); if(false == arg.cancel) @@ -1215,14 +1191,8 @@ namespace detail } } - if (wd_manager.available(root_wd) && root_wd->other.attribute.root->update_requesters.size()) - { - for (auto wd : root_wd->other.attribute.root->update_requesters) - { - window_layout::paint(wd, window_layout::paint_operation::have_refreshed, false); - wd_manager.map(wd, true); - } - } + + wd_manager.update_requesters(root_wd); root_runtime = wd_manager.root_runtime(native_window); if(root_runtime) @@ -1262,10 +1232,10 @@ namespace detail lock.revert(); native_window_type owner_native{}; - core_window_t * owner = 0; + basic_window * owner = nullptr; if(condition_wd && is_modal) { - native_window_type modal = reinterpret_cast(condition_wd)->root; + native_window_type modal = condition_wd->root; owner_native = native_interface::get_window(modal, window_relationship::owner); if(owner_native) { @@ -1276,7 +1246,7 @@ namespace detail } } - nana::detail::platform_spec::instance().msg_dispatch(condition_wd ? reinterpret_cast(condition_wd)->root : 0); + nana::detail::platform_spec::instance().msg_dispatch(condition_wd ? condition_wd->root : 0); if(owner_native) { @@ -1304,7 +1274,7 @@ namespace detail }//end bedrock::event_loop //Dynamically set a cursor for a window - void bedrock::set_cursor(core_window_t* wd, nana::cursor cur, thread_context* thrd) + void bedrock::set_cursor(basic_window* wd, nana::cursor cur, thread_context* thrd) { if (nullptr == thrd) thrd = get_thread_context(wd->thread_id); @@ -1347,14 +1317,14 @@ namespace detail } } - void bedrock::define_state_cursor(core_window_t* wd, nana::cursor cur, thread_context* thrd) + void bedrock::define_state_cursor(basic_window* wd, nana::cursor cur, thread_context* thrd) { wd->root_widget->other.attribute.root->state_cursor = cur; wd->root_widget->other.attribute.root->state_cursor_window = wd; set_cursor(wd, cur, thrd); } - void bedrock::undefine_state_cursor(core_window_t * wd, thread_context* thrd) + void bedrock::undefine_state_cursor(basic_window * wd, thread_context* thrd) { if (!wd_manager().available(wd)) return; diff --git a/source/gui/detail/bedrock_types.hpp b/source/gui/detail/bedrock_types.hpp index f3fb55bc..a95b0755 100644 --- a/source/gui/detail/bedrock_types.hpp +++ b/source/gui/detail/bedrock_types.hpp @@ -18,12 +18,12 @@ namespace nana color_schemes scheme; events_operation evt_operation; window_manager wd_manager; - std::set auto_form_set; + std::set auto_form_set; bool shortkey_occurred{ false }; struct menu_rep { - core_window_t* taken_window{ nullptr }; + basic_window* taken_window{ nullptr }; bool delay_restore{ false }; native_window_type window{ nullptr }; native_window_type owner{ nullptr }; @@ -37,7 +37,7 @@ namespace nana { unsigned event_pump_ref_count{0}; int window_count{0}; //The number of windows - core_window_t* event_window{nullptr}; + basic_window* event_window{nullptr}; struct platform_detail_tag { @@ -46,7 +46,7 @@ namespace nana struct cursor_tag { - core_window_t * window; + basic_window * window; native_window_type native_handle; nana::cursor predef_cursor; HCURSOR handle; @@ -66,7 +66,7 @@ namespace nana unsigned event_pump_ref_count{0}; int window_count{0}; //The number of windows - core_window_t* event_window{nullptr}; + basic_window* event_window{nullptr}; bool is_alt_pressed{false}; bool is_ctrl_pressed{false}; @@ -78,7 +78,7 @@ namespace nana struct cursor_tag { - core_window_t * window; + basic_window * window; native_window_type native_handle; nana::cursor predef_cursor; Cursor handle; diff --git a/source/gui/detail/bedrock_windows.cpp b/source/gui/detail/bedrock_windows.cpp index 2747e65f..829c909a 100644 --- a/source/gui/detail/bedrock_windows.cpp +++ b/source/gui/detail/bedrock_windows.cpp @@ -18,7 +18,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -213,9 +214,9 @@ namespace detail bedrock::~bedrock() { - if(wd_manager().number_of_core_window()) + if(wd_manager().window_count()) { - std::string msg = "Nana.GUI detects a memory leaks in window_manager, " + std::to_string(wd_manager().number_of_core_window()) + " window(s) are not uninstalled."; + std::string msg = "Nana.GUI detects a memory leaks in window_manager, " + std::to_string(wd_manager().window_count()) + " window(s) are not uninstalled."; std::cerr << msg; /// \todo add list of cations of opening windows and if auto testing GUI do auto OK after 2 seconds. ::MessageBoxA(0, msg.c_str(), ("Nana C++ Library"), MB_OK); } @@ -290,7 +291,7 @@ namespace detail return bedrock_object; } - void bedrock::flush_surface(core_window_t* wd, bool forced, const rectangle* update_area) + void bedrock::flush_surface(basic_window* wd, bool forced, const rectangle* update_area) { if (nana::system::this_thread_id() != wd->thread_id) { @@ -311,7 +312,7 @@ namespace detail } } else - wd->drawer.map(reinterpret_cast(wd), forced, update_area); + wd->drawer.map(wd, forced, update_area); } void interior_helper_for_menu(MSG& msg, native_window_type menu_window) @@ -367,7 +368,7 @@ namespace detail MSG msg; if (condition_wd) { - HWND native_handle = reinterpret_cast(reinterpret_cast(condition_wd)->root); + HWND native_handle = reinterpret_cast(condition_wd->root); if (is_modal) { HWND owner = ::GetWindow(native_handle, GW_OWNER); @@ -466,7 +467,7 @@ namespace detail void assign_arg(nana::arg_mouse& arg, basic_window* wd, unsigned msg, const parameter_decoder& pmdec) { - arg.window_handle = reinterpret_cast(wd); + arg.window_handle = wd; bool set_key_state = true; switch (msg) @@ -530,7 +531,7 @@ namespace detail void assign_arg(arg_wheel& arg, basic_window* wd, const parameter_decoder& pmdec) { - arg.window_handle = reinterpret_cast(wd); + arg.window_handle = wd; arg.evt_code = event_code::mouse_wheel; POINT point = { pmdec.mouse.x, pmdec.mouse.y }; @@ -581,7 +582,7 @@ namespace detail case nana::detail::messages::remote_flush_surface: { auto stru = reinterpret_cast(lParam); - bedrock.wd_manager().map(reinterpret_cast(wParam), stru->forced, (stru->ignore_update_area ? nullptr : &stru->update_area)); + bedrock.wd_manager().map(reinterpret_cast(wParam), stru->forced, (stru->ignore_update_area ? nullptr : &stru->update_area)); ::UpdateWindow(wd); ::HeapFree(::GetProcessHeap(), 0, stru); } @@ -652,7 +653,7 @@ namespace detail return true; } - void adjust_sizing(bedrock::core_window_t* wd, ::RECT * const r, int edge, unsigned req_width, unsigned req_height) + void adjust_sizing(basic_window* wd, ::RECT * const r, int edge, unsigned req_width, unsigned req_height) { unsigned width = static_cast(r->right - r->left) - wd->extra_width; unsigned height = static_cast(r->bottom - r->top) - wd->extra_height; @@ -740,27 +741,6 @@ namespace detail return static_cast(vkey); } - class window_proc_guard - { - public: - window_proc_guard(detail::basic_window* wd) : - root_wd_(wd) - { - root_wd_->other.attribute.root->lazy_update = true; - } - - ~window_proc_guard() - { - if (!bedrock::instance().wd_manager().available(root_wd_)) - return; - - root_wd_->other.attribute.root->lazy_update = false; - root_wd_->other.attribute.root->update_requesters.clear(); - } - private: - detail::basic_window* const root_wd_; - }; - LRESULT CALLBACK Bedrock_WIN32_WindowProc(HWND root_window, UINT message, WPARAM wParam, LPARAM lParam) { LRESULT window_proc_value = 0; @@ -793,7 +773,7 @@ namespace detail auto const root_wd = root_runtime->window; auto msgwnd = root_wd; - window_proc_guard wp_guard{ root_wd }; + detail::bedrock::root_guard rw_guard{ brock, root_wd }; switch (message) { @@ -1006,7 +986,7 @@ namespace detail msgwnd->set_action(mouse_action::hovered); if ((::nana::mouse::left_button == arg.button) && (pressed_wd == msgwnd)) { - click_arg.window_handle = reinterpret_cast(msgwnd); + click_arg.window_handle = msgwnd; draw_invoker(&drawer::click, msgwnd, click_arg, &context); } } @@ -1018,16 +998,16 @@ namespace detail draw_invoker(&drawer::mouse_up, msgwnd, arg, &context); if (click_arg.window_handle) - retain->click.emit(click_arg, reinterpret_cast(msgwnd)); + retain->click.emit(click_arg, msgwnd); if (wd_manager.available(msgwnd)) { arg.evt_code = event_code::mouse_up; - retain->mouse_up.emit(arg, reinterpret_cast(msgwnd)); + retain->mouse_up.emit(arg, msgwnd); } } else if (click_arg.window_handle) - retain->click.emit(click_arg, reinterpret_cast(msgwnd)); + retain->click.emit(click_arg, msgwnd); wd_manager.do_lazy_refresh(msgwnd, false); } @@ -1197,9 +1177,9 @@ namespace detail dropfiles.pos = pos; wd_manager.calc_window_point(msgwnd, dropfiles.pos); - dropfiles.window_handle = reinterpret_cast(msgwnd); + dropfiles.window_handle = msgwnd; - msgwnd->annex.events_ptr->mouse_dropfiles.emit(dropfiles, reinterpret_cast(msgwnd)); + msgwnd->annex.events_ptr->mouse_dropfiles.emit(dropfiles, msgwnd); wd_manager.do_lazy_refresh(msgwnd, false); } } @@ -1253,7 +1233,7 @@ namespace detail static_cast(r->bottom - r->top - msgwnd->extra_height)); arg_resizing arg; - arg.window_handle = reinterpret_cast(msgwnd); + arg.window_handle = msgwnd; arg.width = size_before.width; arg.height = size_before.height; @@ -1306,7 +1286,7 @@ namespace detail //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); + msgwnd->drawer.map(msgwnd, true, &update_area); } ::EndPaint(root_window, &ps); } @@ -1321,7 +1301,7 @@ namespace detail arg.evt_code = event_code::shortkey; arg.key = static_cast(wParam < 0x61 ? wParam + 0x61 - 0x41 : wParam); arg.ctrl = arg.shift = false; - arg.window_handle = reinterpret_cast(msgwnd); + arg.window_handle = msgwnd; arg.ignore = false; brock.emit(event_code::shortkey, msgwnd, arg, true, &context); def_window_proc = false; @@ -1337,7 +1317,7 @@ namespace detail bool focused = (brock.focus() == msgwnd); arg_keyboard arg; arg.evt_code = event_code::key_press; - arg.window_handle = reinterpret_cast(msgwnd); + arg.window_handle = msgwnd; arg.ignore = false; arg.key = static_cast(wParam); brock.get_key_state(arg); @@ -1365,7 +1345,7 @@ namespace detail arg_keyboard arg; arg.evt_code = event_code::key_release; - arg.window_handle = reinterpret_cast(msgwnd); + arg.window_handle = msgwnd; arg.ignore = false; arg.key = static_cast(wParam); brock.get_key_state(arg); @@ -1407,7 +1387,7 @@ namespace detail else if ((VK_SPACE == wParam) && msgwnd->flags.space_click_enabled) { //Clicked by spacebar - if (nullptr == pressed_wd && nullptr == pressed_wd_space) + if ((nullptr == pressed_wd) && (nullptr == pressed_wd_space) && msgwnd->flags.enabled) { arg_mouse arg; arg.alt = false; @@ -1418,7 +1398,7 @@ namespace detail arg.mid_button = false; arg.pos.x = 0; arg.pos.y = 0; - arg.window_handle = reinterpret_cast(msgwnd); + arg.window_handle = msgwnd; msgwnd->set_action(mouse_action::pressed); @@ -1433,7 +1413,7 @@ namespace detail { arg_keyboard arg; arg.evt_code = event_code::key_press; - arg.window_handle = reinterpret_cast(msgwnd); + arg.window_handle = msgwnd; arg.ignore = false; arg.key = translate_virtual_key(wParam); brock.get_key_state(arg); @@ -1464,12 +1444,12 @@ namespace detail { arg_keyboard arg; arg.evt_code = event_code::key_char; - arg.window_handle = reinterpret_cast(msgwnd); + arg.window_handle = msgwnd; arg.key = static_cast(wParam); brock.get_key_state(arg); arg.ignore = false; - msgwnd->annex.events_ptr->key_char.emit(arg, reinterpret_cast(msgwnd)); + msgwnd->annex.events_ptr->key_char.emit(arg, msgwnd); if ((false == arg.ignore) && wd_manager.available(msgwnd)) draw_invoker(&drawer::key_char, msgwnd, arg, &context); @@ -1489,7 +1469,7 @@ namespace detail msgwnd = brock.focus(); if (msgwnd) { - if (msgwnd == pressed_wd_space) + if ((msgwnd == pressed_wd_space) && msgwnd->flags.enabled) { msgwnd->set_action(mouse_action::normal); @@ -1497,7 +1477,7 @@ namespace detail arg_click click_arg; click_arg.mouse_args = nullptr; - click_arg.window_handle = reinterpret_cast(msgwnd); + click_arg.window_handle = msgwnd; arg_mouse arg; arg.alt = false; @@ -1508,7 +1488,7 @@ namespace detail arg.mid_button = false; arg.pos.x = 0; arg.pos.y = 0; - arg.window_handle = reinterpret_cast(msgwnd); + arg.window_handle = msgwnd; draw_invoker(&drawer::mouse_up, msgwnd, arg, &context); @@ -1521,7 +1501,7 @@ namespace detail { arg_keyboard keyboard_arg; keyboard_arg.evt_code = event_code::key_release; - keyboard_arg.window_handle = reinterpret_cast(msgwnd); + keyboard_arg.window_handle = msgwnd; keyboard_arg.key = translate_virtual_key(wParam); brock.get_key_state(keyboard_arg); keyboard_arg.ignore = false; @@ -1542,7 +1522,7 @@ namespace detail case WM_CLOSE: { arg_unload arg; - arg.window_handle = reinterpret_cast(msgwnd); + arg.window_handle = msgwnd; arg.cancel = false; brock.emit(event_code::unload, msgwnd, arg, true, &context); if (!arg.cancel) @@ -1577,14 +1557,7 @@ namespace detail def_window_proc = true; } - if (wd_manager.available(root_wd) && root_wd->other.attribute.root->update_requesters.size()) - { - for (auto wd : root_wd->other.attribute.root->update_requesters) - { - window_layout::paint(wd, window_layout::paint_operation::have_refreshed, false); - wd_manager.map(wd, true); - } - } + wd_manager.update_requesters(root_wd); root_runtime = wd_manager.root_runtime(native_window); if(root_runtime) @@ -1666,7 +1639,7 @@ namespace detail return impl_->estore; } - void bedrock::map_through_widgets(core_window_t* wd, native_drawable_type drawable) + void bedrock::map_through_widgets(basic_window* wd, native_drawable_type drawable) { auto graph_context = reinterpret_cast(wd->root_graph->handle()->context); @@ -1713,7 +1686,7 @@ namespace detail } //Dynamically set a cursor for a window - void bedrock::set_cursor(core_window_t* wd, nana::cursor cur, thread_context* thrd) + void bedrock::set_cursor(basic_window* wd, nana::cursor cur, thread_context* thrd) { if (nullptr == thrd) thrd = get_thread_context(wd->thread_id); @@ -1758,7 +1731,7 @@ namespace detail } } - void bedrock::define_state_cursor(core_window_t* wd, nana::cursor cur, thread_context* thrd) + void bedrock::define_state_cursor(basic_window* wd, nana::cursor cur, thread_context* thrd) { wd->root_widget->other.attribute.root->state_cursor = cur; wd->root_widget->other.attribute.root->state_cursor_window = wd; @@ -1768,11 +1741,8 @@ namespace detail ::ShowCursor(TRUE); } - void bedrock::undefine_state_cursor(core_window_t * wd, thread_context* thrd) + void bedrock::undefine_state_cursor(basic_window * wd, thread_context* thrd) { - if (nullptr == thrd) - thrd = get_thread_context(wd->thread_id); - HCURSOR rev_handle = ::LoadCursor(nullptr, IDC_ARROW); if (!wd_manager().available(wd)) { @@ -1781,6 +1751,9 @@ namespace detail return; } + if (nullptr == thrd) + thrd = get_thread_context(wd->thread_id); + wd->root_widget->other.attribute.root->state_cursor = nana::cursor::arrow; wd->root_widget->other.attribute.root->state_cursor_window = nullptr; diff --git a/source/gui/detail/drawer.cpp b/source/gui/detail/drawer.cpp index da5b0091..5b21161b 100644 --- a/source/gui/detail/drawer.cpp +++ b/source/gui/detail/drawer.cpp @@ -1,7 +1,7 @@ /* * A Drawer Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -10,11 +10,11 @@ * @file: nana/gui/detail/drawer.cpp */ +#include "basic_window.hpp" +#include "effects_renderer.hpp" #include #include #include -#include -#include #include "dynamic_drawing_object.hpp" #if defined(NANA_X11) @@ -23,8 +23,6 @@ namespace nana { - typedef detail::edge_nimbus_renderer edge_nimbus_renderer_t; - //class drawer_trigger void drawer_trigger::attached(widget_reference, graph_reference){} void drawer_trigger::detached(){} //none-const @@ -344,8 +342,7 @@ namespace nana { if(wd) { - auto iwd = reinterpret_cast(wd); - bool owns_caret = (iwd->annex.caret_ptr) && (iwd->annex.caret_ptr->visible()); + bool owns_caret = (wd->annex.caret_ptr) && (wd->annex.caret_ptr->visible()); //The caret in X11 is implemented by Nana, it is different from Windows' //the caret in X11 is asynchronous, it is hard to hide and show the caret @@ -354,20 +351,20 @@ namespace nana if(owns_caret) { #ifndef NANA_X11 - iwd->annex.caret_ptr->visible(false); + wd->annex.caret_ptr->visible(false); #else - owns_caret = nana::detail::platform_spec::instance().caret_update(iwd->root, *iwd->root_graph, false); + owns_caret = nana::detail::platform_spec::instance().caret_update(wd->root, *wd->root_graph, false); #endif } - edge_nimbus_renderer_t::instance().render(iwd, forced, update_area); + edge_nimbus_renderer::instance().render(wd, forced, update_area); if(owns_caret) { #ifndef NANA_X11 - iwd->annex.caret_ptr->visible(true); + wd->annex.caret_ptr->visible(true); #else - nana::detail::platform_spec::instance().caret_update(iwd->root, *iwd->root_graph, true); + nana::detail::platform_spec::instance().caret_update(wd->root, *wd->root_graph, true); #endif } } @@ -475,4 +472,192 @@ namespace nana return data_impl_->mth_state[pos]; } }//end namespace detail + + namespace detail + { + //class edge_nimbus_renderer + edge_nimbus_renderer& edge_nimbus_renderer::instance() + { + static edge_nimbus_renderer object; + return object; + } + + void edge_nimbus_renderer::erase(basic_window* wd) + { + if (effects::edge_nimbus::none == wd->effect.edge_nimbus) + return; + + auto root_wd = wd->root_widget; + auto & nimbus = root_wd->other.attribute.root->effects_edge_nimbus; + + for (auto i = nimbus.begin(); i != nimbus.end(); ++i) + { + 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 edge_nimbus_renderer::render(basic_window* wd, bool forced, const rectangle* update_area) + { + 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; + + nana::rectangle r; + for (auto & action : nimbus) + { + if (_m_edge_nimbus(action.window, focused) && window_layer::read_visual_rectangle(action.window, r)) + { + 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)) || (focused == action.window) || !action.rendered || (action.window->other.upd_state == basic_window::update_state::refreshed)) + { + rd_set.emplace_back(r, action.window); + action.rendered = true; + } + } + else if (action.rendered) + { + action.rendered = false; + + 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); + } + } + } + + 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); + } + } + + rectangle wd_r{ wd->pos_root, wd->dimension }; + wd_r.pare_off(-static_cast(this->weight())); + //Render + for (auto & rd : rd_set) + { + auto other_wd = rd.second; + + if (other_wd != wd) + { + rectangle other_r{ other_wd->pos_root, other_wd->dimension }; + other_r.pare_off(-static_cast(this->weight())); + if (!overlapped(wd_r, other_r)) + continue; + } + _m_render_edge_nimbus(other_wd, rd.first); + } + } + + /// Determines whether the effect will be rendered for the given window. + bool edge_nimbus_renderer::_m_edge_nimbus(basic_window * const wd, basic_window * const focused_wd) + { + // Don't render the effect if the window is disabled. + if (wd->flags.enabled) + { + if ((focused_wd == wd) && (static_cast(wd->effect.edge_nimbus) & static_cast(effects::edge_nimbus::active))) + return true; + else if ((static_cast(wd->effect.edge_nimbus) & static_cast(effects::edge_nimbus::over)) && (wd->flags.action == mouse_action::hovered)) + return true; + } + return false; + } + + void edge_nimbus_renderer::_m_render_edge_nimbus(basic_window* wd, const nana::rectangle & visual) + { + wd->flags.action_before = wd->flags.action; + + auto r = visual; + r.pare_off(-static_cast(weight())); + 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.right() > visual.right()) || (good_r.bottom() > visual.bottom())) + { + auto graph = wd->root_graph; + nana::paint::pixel_buffer pixbuf(graph->handle(), r); + + pixel_argb_t px0, px1, px2, px3; + + px0 = pixbuf.pixel(0, 0); + px1 = pixbuf.pixel(r.width - 1, 0); + px2 = pixbuf.pixel(0, r.height - 1); + px3 = pixbuf.pixel(r.width - 1, r.height - 1); + + good_r.x = good_r.y = 1; + good_r.width = r.width - 2; + good_r.height = r.height - 2; + pixbuf.rectangle(good_r, wd->annex.scheme->activated.get_color(), 0.95, false); + + good_r.x = good_r.y = 0; + good_r.width = r.width; + good_r.height = r.height; + pixbuf.rectangle(good_r, wd->annex.scheme->activated.get_color(), 0.4, false); + + pixbuf.pixel(0, 0, px0); + pixbuf.pixel(r.width - 1, 0, px1); + pixbuf.pixel(0, r.height - 1, px2); + pixbuf.pixel(r.width - 1, r.height - 1, px3); + + pixbuf.paste(wd->root, { r.x, r.y }); + + std::vector overlaps; + if (window_layer::read_overlaps(wd, visual, overlaps)) + { + for (auto & wdr : overlaps) + graph->paste(wd->root, wdr.r, wdr.r.x, wdr.r.y); + } + } + else + wd->root_graph->paste(wd->root, visual, visual.x, visual.y); + } + } + + //end class edge_nimbus_renderer + }//end namespace detail }//end namespace nana diff --git a/source/gui/detail/effects_renderer.hpp b/source/gui/detail/effects_renderer.hpp new file mode 100644 index 00000000..99fbf49c --- /dev/null +++ b/source/gui/detail/effects_renderer.hpp @@ -0,0 +1,52 @@ +/* +* Effects Renderer +* Nana C++ Library(http://www.nanapro.org) +* Copyright(C) 2003-2019 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/effects_renderer.cpp +*/ + +#ifndef NANA_GUI_DETAIL_EFFECTS_RENDERER_HPP +#define NANA_GUI_DETAIL_EFFECTS_RENDERER_HPP +#include "basic_window.hpp" +#include +#include +#include +#include +#include + +namespace nana{ + namespace detail + { + /// Effect edige nimbus renderer + class edge_nimbus_renderer + { + edge_nimbus_renderer() = default; + public: + using window_layer = ::nana::detail::window_layout; + using graph_reference = ::nana::paint::graphics&; + + static edge_nimbus_renderer& instance(); + + constexpr unsigned weight() const + { + return 2; + } + + void erase(basic_window* wd); + + void render(basic_window* wd, bool forced, const rectangle* update_area = nullptr); + private: + /// Determines whether the effect will be rendered for the given window. + static bool _m_edge_nimbus(basic_window * const wd, basic_window * const focused_wd); + + void _m_render_edge_nimbus(basic_window* wd, const nana::rectangle & visual); + }; + } +}//end namespace nana + +#endif diff --git a/source/gui/detail/events_operation.cpp b/source/gui/detail/events_operation.cpp index a278bc96..9c7c12dc 100644 --- a/source/gui/detail/events_operation.cpp +++ b/source/gui/detail/events_operation.cpp @@ -26,9 +26,7 @@ namespace nana auto i = handles_.find(evt); if (i != handles_.end()) - { - reinterpret_cast(evt)->get_event()->remove(evt); - } + reinterpret_cast(evt)->get_event()->remove(evt); } //end namespace events_operation @@ -80,35 +78,33 @@ namespace nana void event_base::remove(event_handle evt) { internal_scope_guard lock; - if (dockers_) + + for (auto i = dockers_->begin(), end = dockers_->end(); i != end; ++i) { - for (auto i = dockers_->begin(), end = dockers_->end(); i != end; ++i) + if (reinterpret_cast(evt) == *i) { - if (reinterpret_cast(evt) == *i) + //Checks whether this event is working now. + if (emitting_count_) { - //Checks whether this event is working now. - if (emitting_count_) - { - static_cast(*i)->flag_deleted = true; - deleted_flags_ = true; - } - else - { - bedrock::instance().evt_operation().cancel(evt); - dockers_->erase(i); - delete reinterpret_cast(evt); - } - break; + static_cast(*i)->flag_deleted = true; + deleted_flags_ = true; } + else + { + bedrock::instance().evt_operation().cancel(evt); + dockers_->erase(i); + delete reinterpret_cast(evt); + } + return; } } } - event_handle event_base::_m_emplace(detail::docker_interface* docker_ptr, bool in_front) + event_handle event_base::_m_emplace(detail::event_docker_interface* docker_ptr, bool in_front) { internal_scope_guard lock; if (nullptr == dockers_) - dockers_ = new std::vector; + dockers_ = new std::vector; auto evt = reinterpret_cast(docker_ptr); diff --git a/source/gui/detail/inner_fwd_implement.hpp b/source/gui/detail/inner_fwd_implement.hpp index 5c66323c..af246905 100644 --- a/source/gui/detail/inner_fwd_implement.hpp +++ b/source/gui/detail/inner_fwd_implement.hpp @@ -15,8 +15,8 @@ #define NANA_GUI_INNER_FWD_IMPLEMENT_HPP #include +#include "basic_window.hpp" #include -#include #include #include diff --git a/source/gui/detail/window_layout.cpp b/source/gui/detail/window_layout.cpp index b0187eec..c94f363c 100644 --- a/source/gui/detail/window_layout.cpp +++ b/source/gui/detail/window_layout.cpp @@ -11,8 +11,8 @@ * */ +#include "basic_window.hpp" #include -#include #include #include #include @@ -22,7 +22,7 @@ namespace nana namespace detail { //class window_layout - void window_layout::paint(core_window_t* wd, paint_operation operation, bool req_refresh_children) + void window_layout::paint(basic_window* wd, paint_operation operation, bool req_refresh_children) { if (wd->flags.refreshing && (paint_operation::try_refresh == operation)) return; @@ -41,7 +41,7 @@ namespace nana _m_paint_glass_window(wd, (paint_operation::try_refresh == operation), req_refresh_children, false, true); } - bool window_layout::maproot(core_window_t* wd, bool have_refreshed, bool req_refresh_children) + bool window_layout::maproot(basic_window* wd, bool have_refreshed, bool req_refresh_children) { auto check_opaque = wd->seek_non_lite_widget_ancestor(); if (check_opaque && check_opaque->flags.refreshing) @@ -80,7 +80,7 @@ namespace nana return false; } - void window_layout::paste_children_to_graphics(core_window_t* wd, nana::paint::graphics& graph) + void window_layout::paste_children_to_graphics(basic_window* wd, nana::paint::graphics& graph) { _m_paste_children(wd, false, false, rectangle{ wd->pos_root, wd->dimension }, graph, wd->pos_root); } @@ -89,7 +89,7 @@ namespace nana ///@brief Reads the visual rectangle of a window, the visual rectangle's reference frame is to root widget, /// the visual rectangle is a rectangular block that a window should be displayed on screen. /// The result is a rectangle that is a visible area for its ancestors. - bool window_layout::read_visual_rectangle(core_window_t* wd, nana::rectangle& visual) + bool window_layout::read_visual_rectangle(basic_window* wd, nana::rectangle& visual) { if (! wd->displayed()) return false; @@ -139,7 +139,7 @@ namespace nana //read_overlaps // reads the overlaps that are overlapped a rectangular block - bool window_layout::read_overlaps(core_window_t* wd, const nana::rectangle& vis_rect, std::vector& blocks) + bool window_layout::read_overlaps(basic_window* wd, const nana::rectangle& vis_rect, std::vector& blocks) { auto const is_wd_root = (category::flags::root == wd->other.category); wd_rectangle block; @@ -150,7 +150,7 @@ namespace nana { for (++i; i != wd->parent->children.cend(); ++i) { - core_window_t* cover = *i; + basic_window* cover = *i; if (!cover->visible) continue; @@ -182,7 +182,7 @@ namespace nana return (!blocks.empty()); } - bool window_layout::enable_effects_bground(core_window_t * wd, bool enabled) + bool window_layout::enable_effects_bground(basic_window * wd, bool enabled) { if (category::flags::widget != wd->other.category) return false; @@ -220,15 +220,15 @@ namespace nana //make_bground // update the glass buffer of a glass window. - void window_layout::make_bground(core_window_t* const wd) + void window_layout::make_bground(basic_window* const wd) { nana::point rpos{ wd->pos_root }; auto & glass_buffer = wd->other.glass_buffer; if (category::flags::lite_widget == wd->parent->other.category) { - std::vector layers; - core_window_t * beg = wd->parent; + std::vector layers; + auto beg = wd->parent; while (beg && (category::flags::lite_widget == beg->other.category)) { layers.push_back(beg); @@ -240,11 +240,11 @@ namespace nana nana::rectangle r(wd->pos_owner, wd->dimension); for (auto i = layers.rbegin(), layers_rend = layers.rend(); i != layers_rend; ++i) { - core_window_t * pre = *i; + auto pre = *i; if (false == pre->visible) continue; - core_window_t * term = ((i + 1 != layers_rend) ? *(i + 1) : wd); + auto term = ((i + 1 != layers_rend) ? *(i + 1) : wd); r.position(wd->pos_root - pre->pos_root); for (auto child : pre->children) @@ -286,10 +286,10 @@ namespace nana } if (wd->effect.bground) - wd->effect.bground->take_effect(reinterpret_cast(wd), glass_buffer); + wd->effect.bground->take_effect(wd, glass_buffer); } - void window_layout::_m_paste_children(core_window_t* wd, bool have_refreshed, bool req_refresh_children, const nana::rectangle& parent_rect, nana::paint::graphics& graph, const nana::point& graph_rpos) + void window_layout::_m_paste_children(basic_window* wd, bool have_refreshed, bool req_refresh_children, const nana::rectangle& parent_rect, nana::paint::graphics& graph, const nana::point& graph_rpos) { nana::rectangle rect; for (auto child : wd->children) @@ -333,7 +333,7 @@ namespace nana } } - void window_layout::_m_paint_glass_window(core_window_t* wd, bool is_redraw, bool is_child_refreshed, bool called_by_notify, bool notify_other) + void window_layout::_m_paint_glass_window(basic_window* wd, bool is_redraw, bool is_child_refreshed, bool called_by_notify, bool notify_other) { //A window which has an empty graphics(and lite-widget) does not notify //glass windows for updating their background. @@ -381,7 +381,7 @@ namespace nana /// Notify the glass windows that are overlapped with the specified visual rectangle. /// If a child window of sigwd is a glass window, it doesn't to be notified. - void window_layout::_m_notify_glasses(core_window_t* const sigwd) + void window_layout::_m_notify_glasses(basic_window* const sigwd) { nana::rectangle r_of_sigwd(sigwd->pos_root, sigwd->dimension); for (auto wd : data_sect.effects_bground_windows) @@ -420,7 +420,7 @@ namespace nana else { //test if sigwnd is a parent of glass window x, or a slibing of the glass window, or a child of the slibing of the glass window. - core_window_t *p = wd->parent, *signode = sigwd; + basic_window *p = wd->parent, *signode = sigwd; while (signode->parent && (signode->parent != p)) signode = signode->parent; diff --git a/source/gui/detail/window_manager.cpp b/source/gui/detail/window_manager.cpp index b5842be8..90624598 100644 --- a/source/gui/detail/window_manager.cpp +++ b/source/gui/detail/window_manager.cpp @@ -18,7 +18,8 @@ #include #include #include -#include + +#include "effects_renderer.hpp" #include "window_register.hpp" #include "inner_fwd_implement.hpp" @@ -281,7 +282,7 @@ namespace detail paint::image default_icon_big; paint::image default_icon_small; - lite_map>> safe_place; + lite_map>> safe_place; }; //end struct wdm_private_impl @@ -454,7 +455,7 @@ namespace detail delete impl_; } - std::size_t window_manager::number_of_core_window() const + std::size_t window_manager::window_count() const { //Thread-Safe Required! std::lock_guard lock(mutex_); @@ -467,14 +468,14 @@ namespace detail return mutex_; } - void window_manager::all_handles(std::vector &v) const + void window_manager::all_handles(std::vector &v) const { //Thread-Safe Required! std::lock_guard lock(mutex_); v = impl_->wd_register.queue(); } - void window_manager::event_filter(core_window_t* wd, bool is_make, event_code evtid) + void window_manager::event_filter(basic_window* wd, bool is_make, event_code evtid) { switch(evtid) { @@ -486,21 +487,21 @@ namespace detail } } - bool window_manager::available(core_window_t* wd) + bool window_manager::available(basic_window* wd) { //Thread-Safe Required! std::lock_guard lock(mutex_); return impl_->wd_register.available(wd); } - bool window_manager::available(core_window_t * a, core_window_t* b) + bool window_manager::available(basic_window * a, basic_window* b) { //Thread-Safe Required! std::lock_guard lock(mutex_); return (impl_->wd_register.available(a) && impl_->wd_register.available(b)); } - window_manager::core_window_t* window_manager::create_root(core_window_t* owner, bool nested, rectangle r, const appearance& app, widget* wdg) + basic_window* window_manager::create_root(basic_window* owner, bool nested, rectangle r, const appearance& app, widget* wdg) { native_window_type native = nullptr; if (owner) @@ -524,7 +525,7 @@ namespace detail auto result = native_interface::create_window(native, nested, r, app); if (result.native_handle) { - auto wd = new core_window_t(owner, widget_notifier_interface::get_notifier(wdg), (category::root_tag**)nullptr); + auto wd = new basic_window(owner, widget_notifier_interface::get_notifier(wdg), (category::root_tag**)nullptr); if (nested) { wd->owner = nullptr; @@ -552,7 +553,7 @@ namespace detail return nullptr; } - window_manager::core_window_t* window_manager::create_widget(core_window_t* parent, const rectangle& r, bool is_lite, widget* wdg) + basic_window* window_manager::create_widget(basic_window* parent, const rectangle& r, bool is_lite, widget* wdg) { //Thread-Safe Required! std::lock_guard lock(mutex_); @@ -564,17 +565,17 @@ namespace detail auto wdg_notifier = widget_notifier_interface::get_notifier(wdg); - core_window_t * wd; + basic_window * wd; if (is_lite) - wd = new core_window_t(parent, std::move(wdg_notifier), r, (category::lite_widget_tag**)nullptr); + wd = new basic_window(parent, std::move(wdg_notifier), r, (category::lite_widget_tag**)nullptr); else - wd = new core_window_t(parent, std::move(wdg_notifier), r, (category::widget_tag**)nullptr); + wd = new basic_window(parent, std::move(wdg_notifier), r, (category::widget_tag**)nullptr); impl_->wd_register.insert(wd); return wd; } - void window_manager::close(core_window_t *wd) + void window_manager::close(basic_window *wd) { //Thread-Safe Required! std::lock_guard lock(mutex_); @@ -587,7 +588,7 @@ namespace detail { auto &brock = bedrock::instance(); arg_unload arg; - arg.window_handle = reinterpret_cast(wd); + arg.window_handle = wd; arg.cancel = false; brock.emit(event_code::unload, wd, arg, true, brock.get_thread_context()); if (false == arg.cancel) @@ -616,7 +617,7 @@ namespace detail //destroy //@brief: Delete the window handle - void window_manager::destroy(core_window_t* wd) + void window_manager::destroy(basic_window* wd) { //Thread-Safe Required! std::lock_guard lock(mutex_); @@ -640,7 +641,7 @@ namespace detail update(parent, false, false, &update_area); } - void window_manager::destroy_handle(core_window_t* wd) + void window_manager::destroy_handle(basic_window* wd) { //Thread-Safe Required! std::lock_guard lock(mutex_); @@ -653,7 +654,7 @@ namespace detail } } - void window_manager::icon(core_window_t* wd, const paint::image& small_icon, const paint::image& big_icon) + void window_manager::icon(basic_window* wd, const paint::image& small_icon, const paint::image& big_icon) { if(!big_icon.empty() || !small_icon.empty()) { @@ -676,7 +677,7 @@ namespace detail //show //@brief: show or hide a window - bool window_manager::show(core_window_t* wd, bool visible) + bool window_manager::show(basic_window* wd, bool visible) { //Thread-Safe Required! std::lock_guard lock(mutex_); @@ -701,7 +702,7 @@ namespace detail return true; } - window_manager::core_window_t* window_manager::find_window(native_window_type root, const point& pos, bool ignore_captured) + basic_window* window_manager::find_window(native_window_type root, const point& pos, bool ignore_captured) { if (nullptr == root) return nullptr; @@ -739,7 +740,7 @@ namespace detail } //move the wnd and its all children window, x and y is a relatively coordinate for wnd's parent window - bool window_manager::move(core_window_t* wd, int x, int y, bool passive) + bool window_manager::move(basic_window* wd, int x, int y, bool passive) { //Thread-Safe Required! std::lock_guard lock(mutex_); @@ -758,7 +759,7 @@ namespace detail auto &brock = bedrock::instance(); arg_move arg; - arg.window_handle = reinterpret_cast(wd); + arg.window_handle = wd; arg.x = x; arg.y = y; @@ -788,7 +789,7 @@ namespace detail return false; } - bool window_manager::move(core_window_t* wd, const rectangle& r) + bool window_manager::move(basic_window* wd, const rectangle& r) { //Thread-Safe Required! std::lock_guard lock(mutex_); @@ -812,7 +813,7 @@ namespace detail wd->other.upd_state = basic_window::update_state::request_refresh; arg_move arg; - arg.window_handle = reinterpret_cast(wd); + arg.window_handle = wd; arg.x = r.x; arg.y = r.y; brock.emit(event_code::move, wd, arg, true, brock.get_thread_context()); @@ -846,7 +847,7 @@ namespace detail native_interface::move_window(wd->root, root_r); arg_resized arg; - arg.window_handle = reinterpret_cast(wd); + arg.window_handle = wd; arg.width = root_r.width; arg.height = root_r.height; brock.emit(event_code::resized, wd, arg, true, brock.get_thread_context()); @@ -864,7 +865,7 @@ namespace detail // e.g, when the size of window is changed by OS/user, the function should not resize the // window again, otherwise, it causes an infinite loop, because when a root_widget is resized, // window_manager will call the function. - bool window_manager::size(core_window_t* wd, nana::size sz, bool passive, bool ask_update) + bool window_manager::size(basic_window* wd, nana::size sz, bool passive, bool ask_update) { //Thread-Safe Required! std::lock_guard lock(mutex_); @@ -875,7 +876,7 @@ namespace detail if (sz != wd->dimension) { arg_resizing arg; - arg.window_handle = reinterpret_cast(wd); + arg.window_handle = wd; arg.border = window_border::none; arg.width = sz.width; arg.height = sz.height; @@ -902,7 +903,7 @@ namespace detail if (wd->dimension == sz) return false; - std::vector presence; + std::vector presence; if (wd->dimension.width < sz.width || wd->dimension.height < sz.height) { @@ -970,16 +971,16 @@ namespace detail } arg_resized arg; - arg.window_handle = reinterpret_cast(wd); + arg.window_handle = wd; arg.width = sz.width; arg.height = sz.height; brock.emit(event_code::resized, wd, arg, ask_update, brock.get_thread_context()); return true; } - window_manager::core_window_t* window_manager::root(native_window_type wd) const + basic_window* window_manager::root(native_window_type wd) const { - static std::pair cache; + static std::pair cache; if(cache.first == wd) return cache.second; //Thread-Safe Required! @@ -996,7 +997,7 @@ namespace detail } //Copy the root buffer that wnd specified into DeviceContext - void window_manager::map(core_window_t* wd, bool forced, const rectangle* update_area) + void window_manager::map(basic_window* wd, bool forced, const rectangle* update_area) { //Thread-Safe Required! std::lock_guard lock(mutex_); @@ -1008,7 +1009,7 @@ 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, const rectangle* update_area) + bool window_manager::update(basic_window* wd, bool redraw, bool forced, const rectangle* update_area) { //Thread-Safe Required! std::lock_guard lock(mutex_); @@ -1045,13 +1046,36 @@ namespace detail else if (redraw) window_layer::paint(wd, paint_operation::try_refresh, false); - if (wd->other.upd_state == core_window_t::update_state::lazy) - wd->other.upd_state = core_window_t::update_state::refreshed; + if (wd->other.upd_state == basic_window::update_state::lazy) + wd->other.upd_state = basic_window::update_state::refreshed; } return true; } - void window_manager::refresh_tree(core_window_t* wd) + void window_manager::update_requesters(basic_window* root_wd) + { + //Thread-Safe Required! + std::lock_guard lock(mutex_); + + if (this->available(root_wd) && root_wd->other.attribute.root->update_requesters.size()) + { + for (auto wd : root_wd->other.attribute.root->update_requesters) + { + using paint_operation = window_layer::paint_operation; + if (!this->available(wd)) + continue; + + //#431 + //Redraws the widget when it has beground effect. + //Because the widget just redraw if it didn't have bground effect when it was inserted to the update_requesters queue + window_layer::paint(wd, (wd->effect.bground ? paint_operation::try_refresh : paint_operation::have_refreshed), false); + this->map(wd, true); + } + } + + } + + void window_manager::refresh_tree(basic_window* wd) { //Thread-Safe Required! std::lock_guard lock(mutex_); @@ -1063,7 +1087,7 @@ namespace detail //do_lazy_refresh //@brief: defined a behavior of flush the screen - void window_manager::do_lazy_refresh(core_window_t* wd, bool force_copy_to_screen, bool refresh_tree) + void window_manager::do_lazy_refresh(basic_window* wd, bool force_copy_to_screen, bool refresh_tree) { //Thread-Safe Required! std::lock_guard lock(mutex_); @@ -1077,11 +1101,11 @@ namespace detail using paint_operation = window_layer::paint_operation; if (wd->visible_parents()) { - if ((wd->other.upd_state == core_window_t::update_state::refreshed) || (wd->other.upd_state == core_window_t::update_state::request_refresh) || force_copy_to_screen) + if ((wd->other.upd_state == basic_window::update_state::refreshed) || (wd->other.upd_state == basic_window::update_state::request_refresh) || force_copy_to_screen) { - if (!wd->try_lazy_update(wd->other.upd_state == core_window_t::update_state::request_refresh)) + if (!wd->try_lazy_update(wd->other.upd_state == basic_window::update_state::request_refresh)) { - window_layer::paint(wd, (wd->other.upd_state == core_window_t::update_state::request_refresh ? paint_operation::try_refresh : paint_operation::have_refreshed), refresh_tree); + window_layer::paint(wd, (wd->other.upd_state == basic_window::update_state::request_refresh ? paint_operation::try_refresh : paint_operation::have_refreshed), refresh_tree); this->map(wd, force_copy_to_screen); } } @@ -1100,10 +1124,10 @@ namespace detail window_layer::paint(wd, paint_operation::try_refresh, refresh_tree); //only refreshing if it has an invisible parent } - wd->other.upd_state = core_window_t::update_state::none; + wd->other.upd_state = basic_window::update_state::none; } - bool window_manager::set_parent(core_window_t* wd, core_window_t* newpa) + bool window_manager::set_parent(basic_window* wd, basic_window* newpa) { //Thread-Safe Required! std::lock_guard lock(mutex_); @@ -1130,7 +1154,7 @@ namespace detail //set_focus //@brief: set a keyboard focus to a window. this may fire a focus event. - window_manager::core_window_t* window_manager::set_focus(core_window_t* wd, bool root_has_been_focused, arg_focus::reason reason) + basic_window* window_manager::set_focus(basic_window* wd, bool root_has_been_focused, arg_focus::reason reason) { //Thread-Safe Required! std::lock_guard lock(mutex_); @@ -1154,7 +1178,7 @@ namespace detail prev_focus->annex.caret_ptr->activate(false); arg.getting = false; - arg.window_handle = reinterpret_cast(prev_focus); + arg.window_handle = prev_focus; arg.receiver = wd->root; arg.focus_reason = arg_focus::reason::general; brock.emit(event_code::focus, prev_focus, arg, true, brock.get_thread_context()); @@ -1171,7 +1195,7 @@ namespace detail if(wd->annex.caret_ptr) wd->annex.caret_ptr->activate(true); - arg.window_handle = reinterpret_cast(wd); + arg.window_handle = wd; arg.getting = true; arg.receiver = wd->root; arg.focus_reason = reason; @@ -1194,7 +1218,7 @@ namespace detail return prev_focus; } - window_manager::core_window_t* window_manager::capture_redirect(core_window_t* wd) + basic_window* window_manager::capture_redirect(basic_window* wd) { if(attr_.capture.window && (attr_.capture.ignore_children == false) && (attr_.capture.window != wd)) { @@ -1221,12 +1245,12 @@ namespace detail return false; } - window_manager::core_window_t * window_manager::capture_window() const + basic_window * window_manager::capture_window() const { return attr_.capture.window; } - void window_manager::capture_window(core_window_t* wd, bool captured, bool ignore_children) + void window_manager::capture_window(basic_window* wd, bool captured, bool ignore_children) { if (!this->available(wd)) return; @@ -1262,7 +1286,7 @@ namespace detail wd->flags.captured = false; if(attr_cap.size()) { - std::pair last = attr_cap.back(); + std::pair last = attr_cap.back(); attr_cap.pop_back(); if (impl_->wd_register.available(last.first)) @@ -1297,7 +1321,7 @@ namespace detail // this method insert a window which catches an user TAB into a TAB window container // the TAB window container is held by a wd's root widget. Not every widget has a TAB window container, // the container is created while a first Tab Window is setting - void window_manager::enable_tabstop(core_window_t* wd) + void window_manager::enable_tabstop(basic_window* wd) { //Thread-Safe Required! std::lock_guard lock(mutex_); @@ -1309,7 +1333,7 @@ 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) + basic_window* get_tabstop(basic_window* wd, bool forward) { auto & tabs = wd->root_widget->other.attribute.root->tabstop; @@ -1324,7 +1348,7 @@ namespace detail if (i != end) { ++i; - window_manager::core_window_t* ts = (i != end ? (*i) : tabs.front()); + basic_window* ts = (i != end ? (*i) : tabs.front()); return (ts != wd ? ts : 0); } else @@ -1340,7 +1364,7 @@ namespace detail return nullptr; } - auto window_manager::tabstop(core_window_t* wd, bool forward) const -> core_window_t* + auto window_manager::tabstop(basic_window* wd, bool forward) const -> basic_window* { //Thread-Safe Required! std::lock_guard lock(mutex_); @@ -1384,7 +1408,7 @@ namespace detail impl_->wd_register.delete_trash(tid); } - bool window_manager::enable_effects_bground(core_window_t* wd, bool enabled) + bool window_manager::enable_effects_bground(basic_window* wd, bool enabled) { //Thread-Safe Required! std::lock_guard lock(mutex_); @@ -1394,7 +1418,7 @@ namespace detail return false; } - bool window_manager::calc_window_point(core_window_t* wd, nana::point& pos) + bool window_manager::calc_window_point(basic_window* wd, nana::point& pos) { //Thread-Safe Required! std::lock_guard lock(mutex_); @@ -1414,19 +1438,19 @@ namespace detail return impl_->misc_register.find(native_wd); } - bool window_manager::register_shortkey(core_window_t* wd, unsigned long key) + bool window_manager::register_shortkey(basic_window* wd, unsigned long key) { //Thread-Safe Required! std::lock_guard lock(mutex_); if (impl_->wd_register.available(wd)) { //the root runtime must exist, because the wd is valid. Otherse, it's bug of library - return root_runtime(wd->root)->shortkeys.make(reinterpret_cast(wd), key); + return root_runtime(wd->root)->shortkeys.make(wd, key); } return false; } - void window_manager::unregister_shortkey(core_window_t* wd, bool with_children) + void window_manager::unregister_shortkey(basic_window* wd, bool with_children) { //Thread-Safe Required! std::lock_guard lock(mutex_); @@ -1435,7 +1459,7 @@ namespace detail auto root_rt = root_runtime(wd->root); if (root_rt) { - root_rt->shortkeys.umake(reinterpret_cast(wd)); + root_rt->shortkeys.umake(wd); if (with_children) { for (auto child : wd->children) @@ -1444,7 +1468,7 @@ namespace detail } } - window_manager::core_window_t* window_manager::find_shortkey(native_window_type native_window, unsigned long key) + basic_window* window_manager::find_shortkey(native_window_type native_window, unsigned long key) { if(native_window) { @@ -1452,12 +1476,12 @@ namespace detail std::lock_guard lock(mutex_); auto object = root_runtime(native_window); if(object) - return reinterpret_cast(object->shortkeys.find(key)); + return object->shortkeys.find(key); } return nullptr; } - void window_manager::set_safe_place(core_window_t* wd, std::function&& fn) + void window_manager::set_safe_place(basic_window* wd, std::function&& fn) { if (fn) { @@ -1499,7 +1523,7 @@ namespace detail return false; } - void window_manager::_m_disengage(core_window_t* wd, core_window_t* for_new) + void window_manager::_m_disengage(basic_window* wd, basic_window* for_new) { auto * const wdpa = wd->parent; @@ -1513,7 +1537,7 @@ namespace detail //Holds the shortkeys of wd and its children, and then //register these shortkeys for establishing. - std::vector> sk_holder; + std::vector> sk_holder; if ((!established) || (pa_root_attr != root_attr)) { @@ -1575,10 +1599,8 @@ namespace detail if (!established) { - using effect_renderer = detail::edge_nimbus_renderer; - //remove the window from edge nimbus effect when it is destroying - effect_renderer::instance().erase(wd); + edge_nimbus_renderer::instance().erase(wd); } else if (pa_root_attr != root_attr) { @@ -1634,8 +1656,8 @@ namespace detail auto delta_pos = wd->pos_root - for_new->pos_root; - std::function set_pos_root; - set_pos_root = [&set_pos_root](core_window_t* wd, const nana::point& delta_pos) + std::function set_pos_root; + set_pos_root = [&set_pos_root](basic_window* wd, const nana::point& delta_pos) { for (auto child : wd->children) { @@ -1666,7 +1688,7 @@ namespace detail } } - void window_manager::_m_destroy(core_window_t* wd) + void window_manager::_m_destroy(basic_window* wd) { if(wd->flags.destroying) return; @@ -1682,13 +1704,11 @@ namespace detail wd->annex.caret_ptr = nullptr; } - using effect_renderer = detail::edge_nimbus_renderer; - //remove the window from edge nimbus effect when it is destroying - effect_renderer::instance().erase(wd); + edge_nimbus_renderer::instance().erase(wd); arg_destroy arg; - arg.window_handle = reinterpret_cast(wd); + arg.window_handle = wd; brock.emit(event_code::destroy, wd, arg, true, brock.get_thread_context()); //Delete the children widgets. @@ -1718,7 +1738,7 @@ namespace detail wd->drawer.graphics.release(); } - void window_manager::_m_move_core(core_window_t* wd, const point& delta) + void window_manager::_m_move_core(basic_window* wd, const point& delta) { if(category::flags::root != wd->other.category) //A root widget always starts at (0, 0) and its children are not to be changed { @@ -1740,14 +1760,14 @@ namespace detail } } - void window_manager::_m_shortkeys(core_window_t* wd, bool with_children, std::vector>& keys) const + void window_manager::_m_shortkeys(basic_window* wd, bool with_children, std::vector>& keys) const { if (impl_->wd_register.available(wd)) { //The root_rt must exist, because wd is valid. Otherwise, it's a bug of the library. auto root_rt = root_runtime(wd->root); - auto pkeys = root_rt->shortkeys.keys(reinterpret_cast(wd)); + auto pkeys = root_rt->shortkeys.keys(wd); if (pkeys) { for (auto key : *pkeys) @@ -1765,7 +1785,7 @@ namespace detail //_m_find //@brief: find a window on root window through a given root coordinate. // the given root coordinate must be in the rectangle of wnd. - window_manager::core_window_t* window_manager::_m_find(core_window_t* wd, const point& pos) + basic_window* window_manager::_m_find(basic_window* wd, const point& pos) { if(!wd->visible) return nullptr; @@ -1789,7 +1809,7 @@ namespace detail } //_m_effective, test if the window is a handle of window that specified by (root_x, root_y) - bool window_manager::_m_effective(core_window_t* wd, const point& root_pos) + bool window_manager::_m_effective(basic_window* wd, const point& root_pos) { if(wd == nullptr || false == wd->visible) return false; return rectangle{ wd->pos_root, wd->dimension }.is_hit(root_pos); diff --git a/source/gui/detail/window_register.hpp b/source/gui/detail/window_register.hpp index 7a2500e4..965a1afa 100644 --- a/source/gui/detail/window_register.hpp +++ b/source/gui/detail/window_register.hpp @@ -1,7 +1,7 @@ #ifndef NANA_WINDOW_REGISTER_HEADER_INCLUDED #define NANA_WINDOW_REGISTER_HEADER_INCLUDED -#include +#include "basic_window.hpp" #include #include #include //std::find diff --git a/source/gui/dragdrop.cpp b/source/gui/dragdrop.cpp index 18f51e52..5711fbd5 100644 --- a/source/gui/dragdrop.cpp +++ b/source/gui/dragdrop.cpp @@ -1,7 +1,7 @@ /** * Drag and Drop Implementation * Nana C++ Library(http://www.nanapro.org) -* Copyright(C) 2018 Jinhao(cnjinhao@hotmail.com) +* Copyright(C) 2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -13,9 +13,9 @@ #include #include - #include -#include + +#include "detail/basic_window.hpp" #include #include @@ -157,10 +157,16 @@ namespace nana }; #ifdef NANA_WINDOWS + template - class win32com_iunknown : public Interface + class win32com_iunknown final: public Interface { public: + template + win32com_iunknown(Args&& ... args) : + Interface(std::forward(args)...) + {} + //Implements IUnknown STDMETHODIMP QueryInterface(REFIID riid, void **ppv) { @@ -188,6 +194,7 @@ namespace nana LONG ref_count_{ 1 }; }; + class win32com_drop_target : public IDropTarget, public dragdrop_session { public: @@ -273,296 +280,300 @@ namespace nana DWORD effect_{ DROPEFFECT_NONE }; }; - class drop_source : public win32com_iunknown +class drop_source_impl : public IDropSource +{ +public: + drop_source_impl(window wd) : + window_handle_(wd) + {} + + window source() const { - public: - drop_source(window wd) : - window_handle_(wd) - {} - - window source() const - { - return window_handle_; - } - private: - // IDropSource - STDMETHODIMP QueryContinueDrag(BOOL esc_pressed, DWORD key_state) override - { - if (esc_pressed) - return DRAGDROP_S_CANCEL; - - //Drop the object if left button is released. - if (0 == (key_state & (MK_LBUTTON))) - return DRAGDROP_S_DROP; - - return S_OK; - } - - STDMETHODIMP GiveFeedback(DWORD effect) override - { - return DRAGDROP_S_USEDEFAULTCURSORS; - } - private: - window const window_handle_; - }; - - class win32_dropdata : public win32com_iunknown + return window_handle_; + } +private: + // IDropSource + STDMETHODIMP QueryContinueDrag(BOOL esc_pressed, DWORD key_state) override { - public: - struct data_entry + if (esc_pressed) + return DRAGDROP_S_CANCEL; + + //Drop the object if left button is released. + if (0 == (key_state & (MK_LBUTTON))) + return DRAGDROP_S_DROP; + + return S_OK; + } + + STDMETHODIMP GiveFeedback(DWORD effect) override + { + return DRAGDROP_S_USEDEFAULTCURSORS; + } +private: + window const window_handle_; +}; +using drop_source = win32com_iunknown; + + +class win32_dropdata_impl: public IDataObject +{ +public: + struct data_entry + { + FORMATETC format; + STGMEDIUM medium; + bool read_from; //Indicates the data which is used for reading. + + ~data_entry() { - FORMATETC format; - STGMEDIUM medium; - bool read_from; //Indicates the data which is used for reading. + ::CoTaskMemFree(format.ptd); + ::ReleaseStgMedium(&medium); + } - ~data_entry() - { - ::CoTaskMemFree(format.ptd); - ::ReleaseStgMedium(&medium); - } - - bool compare(const FORMATETC& fmt, bool rdfrom) const - { - return (format.cfFormat == fmt.cfFormat && - (format.tymed & fmt.tymed) != 0 && - (format.dwAspect == DVASPECT_THUMBNAIL || format.dwAspect == DVASPECT_ICON || medium.tymed == TYMED_NULL || format.lindex == fmt.lindex || (format.lindex == 0 && fmt.lindex == -1) || (format.lindex == -1 && fmt.lindex == 0)) && - format.dwAspect == fmt.dwAspect && read_from == rdfrom); - } + bool compare(const FORMATETC& fmt, bool rdfrom) const + { + return (format.cfFormat == fmt.cfFormat && + (format.tymed & fmt.tymed) != 0 && + (format.dwAspect == DVASPECT_THUMBNAIL || format.dwAspect == DVASPECT_ICON || medium.tymed == TYMED_NULL || format.lindex == fmt.lindex || (format.lindex == 0 && fmt.lindex == -1) || (format.lindex == -1 && fmt.lindex == 0)) && + format.dwAspect == fmt.dwAspect && read_from == rdfrom); +} }; - data_entry * find(const FORMATETC& fmt, bool read_from) const + data_entry * find(const FORMATETC& fmt, bool read_from) const + { + data_entry * last_weak_match = nullptr; + + for (auto & entry : entries_) { - data_entry * last_weak_match = nullptr; - - for (auto & entry : entries_) + if (entry->compare(fmt, read_from)) { - if (entry->compare(fmt, read_from)) - { - auto entry_ptd = entry->format.ptd; - if (entry_ptd && fmt.ptd && entry_ptd->tdSize == fmt.ptd->tdSize && (0 == std::memcmp(entry_ptd, fmt.ptd, fmt.ptd->tdSize))) - return entry.get(); - else if (nullptr == entry_ptd && nullptr == fmt.ptd) - return entry.get(); + auto entry_ptd = entry->format.ptd; + if (entry_ptd && fmt.ptd && entry_ptd->tdSize == fmt.ptd->tdSize && (0 == std::memcmp(entry_ptd, fmt.ptd, fmt.ptd->tdSize))) + return entry.get(); + else if (nullptr == entry_ptd && nullptr == fmt.ptd) + return entry.get(); - last_weak_match = entry.get(); - } - } - return last_weak_match; - } - - void assign(const detail::dragdrop_data& data) - { - if (!data.files.empty()) - { - std::size_t bytes = sizeof(wchar_t); - for (auto & file : data.files) - { - auto file_s = file.wstring(); - bytes += (file_s.size() + 1) * sizeof(file_s.front()); - } - - auto hglobal = ::GlobalAlloc(GHND | GMEM_SHARE, sizeof(DROPFILES) + bytes); - - auto dropfiles = reinterpret_cast(::GlobalLock(hglobal)); - dropfiles->pFiles = sizeof(DROPFILES); - dropfiles->fWide = true; - - auto file_buf = reinterpret_cast(dropfiles) + sizeof(DROPFILES); - - for (auto & file : data.files) - { - auto file_s = file.wstring(); - std::memcpy(file_buf, file_s.data(), (file_s.size() + 1) * sizeof(file_s.front())); - file_buf += (file_s.size() + 1) * sizeof(file_s.front()); - } - *reinterpret_cast(file_buf) = 0; - - ::GlobalUnlock(hglobal); - - assign(hglobal); + last_weak_match = entry.get(); } } + return last_weak_match; + } - data_entry* assign(HGLOBAL hglobal) + void assign(const detail::dragdrop_data& data) + { + if (!data.files.empty()) { - FORMATETC fmt = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; - auto entry = find(fmt, true); - if (entry) + std::size_t bytes = sizeof(wchar_t); + for (auto & file : data.files) { - //Free the current entry for reuse - ::CoTaskMemFree(entry->format.ptd); - ::ReleaseStgMedium(&entry->medium); - } - else - { - //Create a new entry - entries_.emplace_back(new data_entry); - entry = entries_.back().get(); + auto file_s = file.wstring(); + bytes += (file_s.size() + 1) * sizeof(file_s.front()); } - //Assign the format to the entry. - entry->read_from = true; - entry->format = fmt; + auto hglobal = ::GlobalAlloc(GHND | GMEM_SHARE, sizeof(DROPFILES) + bytes); - //Assign the stgMedium - entry->medium.tymed = TYMED_HGLOBAL; - entry->medium.hGlobal = hglobal; - entry->medium.pUnkForRelease = nullptr; + auto dropfiles = reinterpret_cast(::GlobalLock(hglobal)); + dropfiles->pFiles = sizeof(DROPFILES); + dropfiles->fWide = true; - return entry; - } - public: - // Implement IDataObject - STDMETHODIMP GetData(FORMATETC *request_format, STGMEDIUM *pmedium) override - { - if (!(request_format && pmedium)) - return E_INVALIDARG; + auto file_buf = reinterpret_cast(dropfiles)+sizeof(DROPFILES); - pmedium->hGlobal = nullptr; - - auto entry = find(*request_format, true); - if (entry) - return _m_copy_medium(pmedium, &entry->medium, &entry->format); - - return DV_E_FORMATETC; - } - - STDMETHODIMP GetDataHere(FORMATETC *pformatetc, STGMEDIUM *pmedium) override - { - return E_NOTIMPL; - } - - STDMETHODIMP QueryGetData(FORMATETC *pformatetc) override - { - if (NULL == pformatetc) - return E_INVALIDARG; - - if (!(DVASPECT_CONTENT & pformatetc->dwAspect)) - return DV_E_DVASPECT; - - HRESULT result = DV_E_TYMED; - - for (auto & entry : entries_) + for (auto & file : data.files) { - if (entry->format.tymed & pformatetc->tymed) - { - if (entry->format.cfFormat == pformatetc->cfFormat) - return S_OK; - - result = DV_E_FORMATETC; - } + auto file_s = file.wstring(); + std::memcpy(file_buf, file_s.data(), (file_s.size() + 1) * sizeof(file_s.front())); + file_buf += (file_s.size() + 1) * sizeof(file_s.front()); } - return result; + *reinterpret_cast(file_buf) = 0; + + ::GlobalUnlock(hglobal); + + assign(hglobal); } + } - STDMETHODIMP GetCanonicalFormatEtc(FORMATETC *pformatectIn, FORMATETC *pformatetcOut) override + data_entry* assign(HGLOBAL hglobal) + { + FORMATETC fmt = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; + auto entry = find(fmt, true); + if (entry) { - return E_NOTIMPL; + //Free the current entry for reuse + ::CoTaskMemFree(entry->format.ptd); + ::ReleaseStgMedium(&entry->medium); } - - STDMETHODIMP SetData(FORMATETC *pformatetc, STGMEDIUM *pmedium, BOOL fRelease) override + else { - if (!(pformatetc && pmedium)) - return E_INVALIDARG; - - if (pformatetc->tymed != pmedium->tymed) - return E_FAIL; - + //Create a new entry entries_.emplace_back(new data_entry); - auto entry = entries_.back().get(); - - entry->format = *pformatetc; - - _m_copy_medium(&entry->medium, pmedium, pformatetc); - - if (TRUE == fRelease) - ::ReleaseStgMedium(pmedium); - - return S_OK; + entry = entries_.back().get(); } - STDMETHODIMP EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC **ppenumFormatEtc) override + //Assign the format to the entry. + entry->read_from = true; + entry->format = fmt; + + //Assign the stgMedium + entry->medium.tymed = TYMED_HGLOBAL; + entry->medium.hGlobal = hglobal; + entry->medium.pUnkForRelease = nullptr; + + return entry; + } +public: + // Implement IDataObject + STDMETHODIMP GetData(FORMATETC *request_format, STGMEDIUM *pmedium) override + { + if (!(request_format && pmedium)) + return E_INVALIDARG; + + pmedium->hGlobal = nullptr; + + auto entry = find(*request_format, true); + if (entry) + return _m_copy_medium(pmedium, &entry->medium, &entry->format); + + return DV_E_FORMATETC; + } + + STDMETHODIMP GetDataHere(FORMATETC *pformatetc, STGMEDIUM *pmedium) override + { + return E_NOTIMPL; + } + + STDMETHODIMP QueryGetData(FORMATETC *pformatetc) override + { + if (NULL == pformatetc) + return E_INVALIDARG; + + if (!(DVASPECT_CONTENT & pformatetc->dwAspect)) + return DV_E_DVASPECT; + + HRESULT result = DV_E_TYMED; + + for (auto & entry : entries_) { - if (NULL == ppenumFormatEtc) + if (entry->format.tymed & pformatetc->tymed) + { + if (entry->format.cfFormat == pformatetc->cfFormat) + return S_OK; + + result = DV_E_FORMATETC; + } + } + return result; + } + + STDMETHODIMP GetCanonicalFormatEtc(FORMATETC *pformatectIn, FORMATETC *pformatetcOut) override + { + return E_NOTIMPL; + } + + STDMETHODIMP SetData(FORMATETC *pformatetc, STGMEDIUM *pmedium, BOOL fRelease) override + { + if (!(pformatetc && pmedium)) + return E_INVALIDARG; + + if (pformatetc->tymed != pmedium->tymed) + return E_FAIL; + + entries_.emplace_back(new data_entry); + auto entry = entries_.back().get(); + + entry->format = *pformatetc; + + _m_copy_medium(&entry->medium, pmedium, pformatetc); + + if (TRUE == fRelease) + ::ReleaseStgMedium(pmedium); + + return S_OK; + } + + STDMETHODIMP EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC **ppenumFormatEtc) override + { + if (NULL == ppenumFormatEtc) + return E_INVALIDARG; + + if (DATADIR_GET != dwDirection) + return E_NOTIMPL; + + *ppenumFormatEtc = nullptr; + + FORMATETC rgfmtetc[] = + { + //{ CF_UNICODETEXT, nullptr, DVASPECT_CONTENT, 0, TYMED_HGLOBAL } + { CF_HDROP, nullptr, DVASPECT_CONTENT, 0, TYMED_HGLOBAL } + }; + return ::SHCreateStdEnumFmtEtc(ARRAYSIZE(rgfmtetc), rgfmtetc, ppenumFormatEtc); + } + + STDMETHODIMP DAdvise(FORMATETC *pformatetc, DWORD advf, IAdviseSink *pAdvSink, DWORD *pdwConnection) override + { + return OLE_E_ADVISENOTSUPPORTED; + } + + STDMETHODIMP DUnadvise(DWORD dwConnection) override + { + return OLE_E_ADVISENOTSUPPORTED; + } + + STDMETHODIMP EnumDAdvise(IEnumSTATDATA **ppenumAdvise) override + { + return OLE_E_ADVISENOTSUPPORTED; + } +private: + static HRESULT _m_copy_medium(STGMEDIUM* stgmed_dst, STGMEDIUM* stgmed_src, FORMATETC* fmt_src) + { + if (!(stgmed_dst && stgmed_src && fmt_src)) + return E_INVALIDARG; + + switch (stgmed_src->tymed) + { + case TYMED_HGLOBAL: + stgmed_dst->hGlobal = (HGLOBAL)OleDuplicateData(stgmed_src->hGlobal, fmt_src->cfFormat, 0); + break; + case TYMED_GDI: + case TYMED_ENHMF: + //GDI object can't be copied to an existing HANDLE + if (stgmed_dst->hGlobal) return E_INVALIDARG; - if (DATADIR_GET != dwDirection) - return E_NOTIMPL; - - *ppenumFormatEtc = nullptr; - - FORMATETC rgfmtetc[] = - { - //{ CF_UNICODETEXT, nullptr, DVASPECT_CONTENT, 0, TYMED_HGLOBAL } - { CF_HDROP, nullptr, DVASPECT_CONTENT, 0, TYMED_HGLOBAL } - }; - return ::SHCreateStdEnumFmtEtc(ARRAYSIZE(rgfmtetc), rgfmtetc, ppenumFormatEtc); + stgmed_dst->hGlobal = (HBITMAP)OleDuplicateData(stgmed_src->hGlobal, fmt_src->cfFormat, 0); + break; + case TYMED_MFPICT: + stgmed_dst->hMetaFilePict = (HMETAFILEPICT)OleDuplicateData(stgmed_src->hMetaFilePict, fmt_src->cfFormat, 0); + break; + case TYMED_FILE: + stgmed_dst->lpszFileName = (LPOLESTR)OleDuplicateData(stgmed_src->lpszFileName, fmt_src->cfFormat, 0); + break; + case TYMED_ISTREAM: + stgmed_dst->pstm = stgmed_src->pstm; + stgmed_src->pstm->AddRef(); + break; + case TYMED_ISTORAGE: + stgmed_dst->pstg = stgmed_src->pstg; + stgmed_src->pstg->AddRef(); + break; + case TYMED_NULL: + default: + break; } - - STDMETHODIMP DAdvise(FORMATETC *pformatetc, DWORD advf, IAdviseSink *pAdvSink, DWORD *pdwConnection) override + stgmed_dst->tymed = stgmed_src->tymed; + stgmed_dst->pUnkForRelease = nullptr; + if (stgmed_src->pUnkForRelease) { - return OLE_E_ADVISENOTSUPPORTED; + stgmed_dst->pUnkForRelease = stgmed_src->pUnkForRelease; + stgmed_src->pUnkForRelease->AddRef(); } - - STDMETHODIMP DUnadvise(DWORD dwConnection) override - { - return OLE_E_ADVISENOTSUPPORTED; - } - - STDMETHODIMP EnumDAdvise(IEnumSTATDATA **ppenumAdvise) override - { - return OLE_E_ADVISENOTSUPPORTED; - } - private: - static HRESULT _m_copy_medium(STGMEDIUM* stgmed_dst, STGMEDIUM* stgmed_src, FORMATETC* fmt_src) - { - if (!(stgmed_dst && stgmed_src && fmt_src)) - return E_INVALIDARG; - - switch (stgmed_src->tymed) - { - case TYMED_HGLOBAL: - stgmed_dst->hGlobal = (HGLOBAL)OleDuplicateData(stgmed_src->hGlobal, fmt_src->cfFormat, 0); - break; - case TYMED_GDI: - case TYMED_ENHMF: - //GDI object can't be copied to an existing HANDLE - if (stgmed_dst->hGlobal) - return E_INVALIDARG; - - stgmed_dst->hGlobal = (HBITMAP)OleDuplicateData(stgmed_src->hGlobal, fmt_src->cfFormat, 0); - break; - case TYMED_MFPICT: - stgmed_dst->hMetaFilePict = (HMETAFILEPICT)OleDuplicateData(stgmed_src->hMetaFilePict, fmt_src->cfFormat, 0); - break; - case TYMED_FILE: - stgmed_dst->lpszFileName = (LPOLESTR)OleDuplicateData(stgmed_src->lpszFileName, fmt_src->cfFormat, 0); - break; - case TYMED_ISTREAM: - stgmed_dst->pstm = stgmed_src->pstm; - stgmed_src->pstm->AddRef(); - break; - case TYMED_ISTORAGE: - stgmed_dst->pstg = stgmed_src->pstg; - stgmed_src->pstg->AddRef(); - break; - case TYMED_NULL: - default: - break; - } - stgmed_dst->tymed = stgmed_src->tymed; - stgmed_dst->pUnkForRelease = nullptr; - if (stgmed_src->pUnkForRelease) - { - stgmed_dst->pUnkForRelease = stgmed_src->pUnkForRelease; - stgmed_src->pUnkForRelease->AddRef(); - } - return S_OK; - } - private: - std::vector> entries_; + return S_OK; + } +private: + std::vector> entries_; }; +using win32_dropdata = win32com_iunknown; + #elif defined(NANA_X11) class x11_dropdata @@ -710,7 +721,7 @@ namespace nana i->second->set_current_source(drag_wd); DWORD result_effect{ DROPEFFECT_NONE }; - auto status = ::DoDragDrop(dropdata, drop_src, DROPEFFECT_COPY, &result_effect); + ::DoDragDrop(dropdata, drop_src, DROPEFFECT_COPY, &result_effect); i->second->set_current_source(nullptr); @@ -961,8 +972,7 @@ namespace nana if (API::is_window(window_handle)) { dragging = true; - auto real_wd = reinterpret_cast(window_handle); - real_wd->other.dnd_state = dragdrop_status::not_ready; + window_handle->other.dnd_state = dragdrop_status::not_ready; } API::release_capture(window_handle); @@ -1000,9 +1010,7 @@ namespace nana if (arg.is_left_button() && API::is_window(impl_->window_handle)) { impl_->dragging = ((!impl_->predicate) || impl_->predicate()); - - auto real_wd = reinterpret_cast(impl_->window_handle); - real_wd->other.dnd_state = dragdrop_status::ready; + impl_->window_handle->other.dnd_state = dragdrop_status::ready; } }); @@ -1010,14 +1018,13 @@ namespace nana if (!(arg.is_left_button() && impl_->dragging && API::is_window(arg.window_handle))) return; - auto real_wd = reinterpret_cast(arg.window_handle); - real_wd->other.dnd_state = dragdrop_status::in_progress; + arg.window_handle->other.dnd_state = dragdrop_status::in_progress; std::unique_ptr dropdata{new dragdrop_service::dropdata_type}; auto has_dropped = dragdrop_service::instance().dragdrop(arg.window_handle, dropdata.get(), nullptr); - real_wd->other.dnd_state = dragdrop_status::not_ready; + arg.window_handle->other.dnd_state = dragdrop_status::not_ready; impl_->dragging = false; if (has_dropped) @@ -1116,9 +1123,7 @@ namespace nana if (arg.is_left_button() && API::is_window(impl_->source_handle)) { impl_->dragging = ((!impl_->predicate) || impl_->predicate()); - - auto real_wd = reinterpret_cast(impl_->source_handle); - real_wd->other.dnd_state = dragdrop_status::ready; + impl_->source_handle->other.dnd_state = dragdrop_status::ready; } }); @@ -1126,13 +1131,9 @@ namespace nana if (!(arg.is_left_button() && impl_->dragging && API::is_window(arg.window_handle))) return; - auto real_wd = reinterpret_cast(arg.window_handle); - real_wd->other.dnd_state = dragdrop_status::in_progress; - + arg.window_handle->other.dnd_state = dragdrop_status::in_progress; impl_->make_drop(); - - - real_wd->other.dnd_state = dragdrop_status::not_ready; + arg.window_handle->other.dnd_state = dragdrop_status::not_ready; impl_->dragging = false; }); } diff --git a/source/gui/drawing.cpp b/source/gui/drawing.cpp index 2e6e2142..9b1fb7e6 100644 --- a/source/gui/drawing.cpp +++ b/source/gui/drawing.cpp @@ -10,9 +10,9 @@ * @file: nana/gui/drawing.cpp */ +#include "detail/basic_window.hpp" #include #include -#include namespace nana { @@ -22,11 +22,9 @@ namespace nana { namespace { - using core_window_t = detail::basic_window; - inline detail::drawer& get_drawer(window wd) { - return reinterpret_cast(wd)->drawer; + return wd->drawer; } } } @@ -38,7 +36,7 @@ namespace nana if (!API::is_window(wd)) throw std::invalid_argument("drawing: invalid window parameter"); - if (reinterpret_cast(wd)->is_draw_through()) + if (wd->is_draw_through()) throw std::invalid_argument("drawing: the window is draw_through enabled"); } @@ -46,7 +44,7 @@ namespace nana bool drawing::empty() const { - return API::empty_window(handle_) || reinterpret_cast(handle_)->root_graph->empty(); + return API::empty_window(handle_) || handle_->root_graph->empty(); } void drawing::update() const diff --git a/source/gui/filebox.cpp b/source/gui/filebox.cpp index b748b5cf..06dfed84 100644 --- a/source/gui/filebox.cpp +++ b/source/gui/filebox.cpp @@ -1062,6 +1062,8 @@ namespace nana { if(text.length() == start_pos) return files; + else if(0 == start_pos) //single selection + return {text}; return {}; } diff --git a/source/gui/msgbox.cpp b/source/gui/msgbox.cpp index d11816fa..9d0467d0 100644 --- a/source/gui/msgbox.cpp +++ b/source/gui/msgbox.cpp @@ -12,7 +12,10 @@ * James Bremner */ -#include +#include +#include +#include +#include #include #include #include diff --git a/source/gui/place.cpp b/source/gui/place.cpp index 968d8736..b9acf02e 100644 --- a/source/gui/place.cpp +++ b/source/gui/place.cpp @@ -1,14 +1,14 @@ -/* +/** * An Implementation of Place for Layout * Nana C++ Library(http://www.nanapro.org) * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at + * (See accompanying file LICENSE or copy at * http://www.boost.org/LICENSE_1_0.txt) * - * @file: nana/gui/place.cpp - * @contributors: Ariel Vina-Rodriguez + * @file nana/gui/place.cpp + * @contributors Ariel Vina-Rodriguez * dankan1890(PR#156) */ @@ -36,16 +36,16 @@ namespace nana { - namespace place_parts + struct badname: place::error { - //check the name - void check_field_name(const char* name) - { - if (*name && (*name != '_' && !(('a' <= *name && *name <= 'z') || ('A' <= *name && *name <= 'Z')))) - throw std::invalid_argument("nana.place: bad field name"); - } - }//end namespace place_parts - + explicit badname( ::std::string what, + const place& plc, + const char* name = "unknown", + std::string::size_type pos = std::string::npos) + :place::error(what + ": bad field name '" + (name ? name : "nullptr") + "'.", + plc, (name ? name : "nullptr"), pos) + {} + }; typedef place_parts::number_t number_t; typedef place_parts::repeated_array repeated_array; @@ -54,6 +54,20 @@ namespace nana class tokenizer { public: + /// \todo add member full_what and overrider what() in internal exeptions + struct error : std::invalid_argument + { + error(std::string what, + const tokenizer& tok) + + : std::invalid_argument{ what + " from tokenizer " }, + pos{tok.pos()}, + div_str(tok.divstr_) + {} + std::string::size_type pos; + std::string div_str; + }; + enum class token { div_start, div_end, splitter, @@ -73,7 +87,7 @@ namespace nana return idstr_; } - const number_t& number() const + const number_t& number() const noexcept { return number_; } @@ -93,9 +107,9 @@ namespace nana return parameters_; } - std::size_t pos() const noexcept + std::string::size_type pos() const noexcept { - return (sp_ - divstr_); + return static_cast(sp_ - divstr_); } token read() @@ -137,9 +151,13 @@ namespace nana while (true) { sp_ = _m_eat_whitespace(sp_); - auto tk = read(); - if (token::number != tk && token::variable != tk && token::repeated != tk) - _m_throw_error("invalid array element"); + auto tk = read(); // try ?? + + if ( token::number != tk + && token::variable != tk + && token::repeated != tk) + + throw error("invalid array element. Expected a number, variable or repaet", *this); if (!repeated) { @@ -165,7 +183,7 @@ namespace nana return (repeated ? token::reparray : token::array); if (ch != ',') - _m_throw_error("invalid array"); + throw error("invalid array", *this); } } break; @@ -183,7 +201,7 @@ namespace nana if (token::number == read()) parameters_.push_back(number_); else - _m_throw_error("invalid parameter."); + throw error("invalid parameter. Expected a number", *this); sp_ = _m_eat_whitespace(sp_); char ch = *sp_++; @@ -192,7 +210,7 @@ namespace nana return token::parameters; if (ch != ',') - _m_throw_error("invalid parameter."); + throw error("invalid parameter. Expected a ','", *this); } break; case '.': case '-': @@ -211,7 +229,7 @@ namespace nana return token::number; } else - _m_throw_error("invalid character '" + std::string(1, *sp_) + "'"); + throw error("invalid character '" + std::string(1, *sp_) + "'", *this); break; default: if ('0' <= *sp_ && *sp_ <= '9') @@ -269,14 +287,14 @@ namespace nana { auto idstr = idstr_; if (token::equal != read()) - _m_throw_error("an equal sign is required after '" + idstr + "'"); + throw error("an equal sign is required after '" + idstr + "'", *this); return ('g' == idstr[0] ? token::grid : token::margin); } else if ("collapse" == idstr_) { if (token::parameters != read()) - _m_throw_error("a parameter list is required after 'collapse'"); + throw error("a parameter list is required after 'collapse'", *this); return token::collapse; } else if (!idstr_.empty()) @@ -333,22 +351,14 @@ namespace nana return token::identifier; } - std::string err = "an invalid character '"; - err += *sp_; - err += "'"; - _m_throw_error(err); + throw error("invalid character '" + std::string(1, *sp_) + "'", *this); return token::error; //Useless, just for syntax correction. } private: - void _m_throw_error(const std::string& err) - { - throw std::runtime_error("nana::place: " + err + " at " + std::to_string(static_cast(sp_ - divstr_))); - } - void _m_attr_number_value() { if (token::equal != read()) - _m_throw_error("an equal sign is required after '" + idstr_ + "'"); + throw error("an equal sign is required after '" + idstr_ + "'", *this); auto p = _m_eat_whitespace(sp_); @@ -358,7 +368,7 @@ namespace nana auto len = _m_number(p, neg_ptr != p); if (0 == len) - _m_throw_error("the '" + idstr_ + "' requires a number(integer or real or percent)"); + throw error("the '" + idstr_ + "' requires a number (integer, real or percent)", *this); sp_ += len + (p - sp_); } @@ -367,7 +377,7 @@ namespace nana { auto idstr = idstr_; if (token::equal != read()) - _m_throw_error("an equal sign is required after '" + idstr + "'"); + throw error("an equal sign is required after '" + idstr + "'", *this); sp_ = _m_eat_whitespace(sp_); @@ -385,19 +395,21 @@ namespace nana case token::reparray: break; default: - _m_throw_error("a (repeated) array is required after '" + idstr + "'"); + throw error("a (repeated) array is required after '" + idstr + "'", *this); } } static const char* _m_eat_whitespace(const char* sp) noexcept { - while (*sp && !std::isgraph(*sp)) + while (*sp && !std::isgraph(int(*sp))) ++sp; return sp; } std::size_t _m_number(const char* sp, bool negative) noexcept { + /// \todo use std::from_char() etc. + const char* allstart = sp; sp = _m_eat_whitespace(sp); @@ -462,8 +474,8 @@ namespace nana return 0; } private: - const char* divstr_; - const char* sp_; + const char* divstr_{}; + const char* sp_{}; std::string idstr_; number_t number_; std::vector array_; @@ -481,8 +493,7 @@ namespace nana { return (vert ? pos.y : pos.x); } - - + static bool is_idchar(int ch) noexcept { return ('_' == ch || std::isalnum(ch)); @@ -490,6 +501,7 @@ namespace nana static std::size_t find_idstr(const std::string& text, const char* idstr, std::size_t off = 0) { + if (!idstr) return text.npos; /// ?? const auto len = std::strlen(idstr); size_t pos; @@ -596,6 +608,20 @@ namespace nana //struct implement struct place::implement { + /// usefull ?? + struct error : std::invalid_argument + { + error(std::string what, + std::string field = "unknown", + std::string::size_type pos = std::string::npos) + + : std::invalid_argument{ what + " from place implementation " }, + pos{ pos }, + field(field.empty() ? "unnamed" : field) + {} + std::string::size_type pos; + std::string field; + }; class field_gather; class field_dock; @@ -643,6 +669,8 @@ namespace nana : public place::field_interface { public: + + /// \todo introduce a place::implement::field_gather::error ?? struct element_t { window handle; @@ -692,13 +720,13 @@ namespace nana return nullptr; } private: - void _m_insert_widget(window wd, bool to_fasten) + void _m_insert_widget(window wd, bool to_fasten) /// \todo better errors caption of failed windows, field { if (API::empty_window(wd)) - throw std::invalid_argument("Place: An invalid window handle."); + throw place::error("Failed to insert an invalid window handle.", *place_ptr_); if (API::get_parent_window(wd) != place_ptr_->window_handle()) - throw std::invalid_argument("Place: the window is not a child of place bind window"); + throw place::error("Failed to insert a window which is not a child of the place-binded window", *place_ptr_); //Listen to destroy of a window //It will delete the element and recollocate when the window destroyed. @@ -1984,11 +2012,11 @@ namespace nana return; auto fieldstr = div.substr(bound.first, bound.second - bound.first); - _m_remove_attr(fieldstr, "weight"); + _m_remove_attr(fieldstr, "weight"); /// \todo and higth, width ? std::string::size_type tag_pos{ left ? div.find('<', bound.second + 2) : div.rfind('>', bound.first - 2) }; if (div.npos == tag_pos) - throw std::invalid_argument("please report an issue if it throws"); + throw place::implement::error("please report an issue: the splitter was unable to update division " + div, name); auto other_bound = get_field_boundary(div, tag_pos); @@ -2704,7 +2732,7 @@ namespace nana return static_cast(arg.real()); const char* pos_strs[] = { "1st", "2nd", "3rd", "4th" }; - throw std::invalid_argument("nana.place: the type of the " + std::string{pos_strs[pos]} +"th parameter for collapse should be integer."); + throw place_parts::tokenizer::error("the type of the " + std::string{pos_strs[pos]} +" parameter for collapse should be integer.", tknizer); } //implicitly_started indicates whether the field in div-text starts without < mark. @@ -2732,82 +2760,83 @@ namespace nana bool invisible = false; token tk = token::eof; - for (tk = tknizer.read(); (tk != token::eof && tk != token::div_end); tk = tknizer.read()) - { - switch (tk) + try { + for (tk = tknizer.read(); (tk != token::eof && tk != token::div_end); tk = tknizer.read()) { - case token::dock: - if (token::eof != div_type && token::dock != div_type) - throw std::invalid_argument("nana.place: conflict of div type at " + std::to_string(tknizer.pos())); - - div_type = token::dock; - break; - case token::fit: - fit = fit_policy::both; - break; - case token::hfit: - case token::vfit: - fit = (token::hfit == tk ? fit_policy::horz : fit_policy::vert); - fit_parameters = tknizer.reparray(); - break; - case token::splitter: - //Ignore the splitter when there is not a division. - if (!children.empty() && (division::kind::splitter != children.back()->kind_of_division)) + switch (tk) { - auto splitter = new div_splitter(tknizer.number(), this); - children.back()->div_next = splitter; + case token::dock: + if (token::eof != div_type && token::dock != div_type) + throw std::invalid_argument("conflict of div type -expected dock type-"); - //Hides the splitter if its left leaf is undisplayed. - if (!children.back()->display) - splitter->display = false; + div_type = token::dock; + break; + case token::fit: + fit = fit_policy::both; + break; + case token::hfit: + case token::vfit: + fit = (token::hfit == tk ? fit_policy::horz : fit_policy::vert); + fit_parameters = tknizer.reparray(); + break; + case token::splitter: + //Ignore the splitter when there is not a division. + if (!children.empty() && (division::kind::splitter != children.back()->kind_of_division)) + { + auto splitter = new div_splitter(tknizer.number(), this); + children.back()->div_next = splitter; - children.emplace_back(std::unique_ptr{ splitter }); + //Hides the splitter if its left leaf is undisplayed. + if (!children.back()->display) + splitter->display = false; + + children.emplace_back(std::unique_ptr{ splitter }); + } + break; + case token::div_start: + { + auto div = scan_div(tknizer, false, ignore_duplicate); + if (!children.empty()) + { + //Hides the splitter if its right leaf is undisplayed. + if ((children.back()->kind_of_division == division::kind::splitter) && !div->display) + children.back()->display = false; + + children.back()->div_next = div.get(); + } + + children.emplace_back(std::move(div)); } break; - case token::div_start: - { - auto div = scan_div(tknizer, false, ignore_duplicate); - if (!children.empty()) - { - //Hides the splitter if its right leaf is undisplayed. - if ((children.back()->kind_of_division == division::kind::splitter) && !div->display) - children.back()->display = false; - - children.back()->div_next = div.get(); - } - - children.emplace_back(std::move(div)); - } - break; - case token::switchable: - div_type = token::switchable; - break; - case token::vert: - div_type = tk; - break; - case token::grid: - div_type = tk; - switch (tknizer.read()) - { - case token::number: - array.push_back(tknizer.number()); - array.push_back(tknizer.number()); + case token::switchable: + div_type = token::switchable; break; - case token::array: - tknizer.array().swap(array); + case token::vert: + div_type = tk; break; - case token::reparray: - array.push_back(tknizer.reparray().at(0)); - array.push_back(tknizer.reparray().at(1)); + case token::grid: + div_type = tk; + switch (tknizer.read()) + { + case token::number: + array.push_back(tknizer.number()); + array.push_back(tknizer.number()); + break; + case token::array: + tknizer.array().swap(array); + break; + case token::reparray: + array.push_back(tknizer.reparray().at(0)); + array.push_back(tknizer.reparray().at(1)); + break; + default: + break; + } break; - default: - break; - } - break; - case token::collapse: + case token::collapse: { if (tknizer.parameters().size() != 4) - throw std::invalid_argument("nana.place: collapse requires 4 parameters."); + throw std::invalid_argument("collapse requires 4 parameters"); ::nana::rectangle col{ get_parameter(tknizer, 0), @@ -2822,7 +2851,7 @@ namespace nana { //Overwrite if a exist_col in collapses has same position as the col. bool use_col = true; - for (auto & exist_col : collapses) + for (auto& exist_col : collapses) { if (exist_col.x == col.x && exist_col.y == col.y) { @@ -2836,7 +2865,7 @@ namespace nana } } break; - case token::weight: case token::min_px: case token::max_px: case token::width: case token::height: + case token::weight: case token::min_px: case token::max_px: case token::width: case token::height: { auto n = tknizer.number(); //If n is the type of real, convert it to integer. @@ -2847,7 +2876,7 @@ namespace nana switch (tk) { case token::weight: weight = n; weight_type = token::weight; break; // we could detect errors here (redefinitions and duplicates) - case token::width : weight = n; weight_type = token::width ; break; + case token::width: weight = n; weight_type = token::width; break; case token::height: weight = n; weight_type = token::height; break; case token::min_px: min_px = n; break; case token::max_px: max_px = n; break; @@ -2855,228 +2884,246 @@ namespace nana } } break; - case token::arrange: - arrange = tknizer.reparray(); - break; - case token::gap: - gap = tknizer.reparray(); - break; - case token::margin: - margin.clear(); - switch (tknizer.read()) - { - case token::number: - margin.push(tknizer.number(), true); + case token::arrange: + arrange = tknizer.reparray(); break; - case token::array: - margin.set_array(tknizer.array()); + case token::gap: + gap = tknizer.reparray(); break; - case token::reparray: - for (std::size_t i = 0; i < 4; ++i) + case token::margin: + margin.clear(); + switch (tknizer.read()) { - auto n = tknizer.reparray().at(i); - if (n.empty()) n.assign(0); - margin.push(n); - } - break; - default: - break; - } - break; - case token::identifier: - name = tknizer.idstr(); - break; - case token::left: - div_dir = ::nana::direction::west; break; - case token::right: - div_dir = ::nana::direction::east; break; - case token::top: - div_dir = ::nana::direction::north; break; - case token::bottom: - div_dir = ::nana::direction::south; break; - case token::undisplayed: - undisplayed = true; break; - case token::invisible: - invisible = true; break; - default: break; - } - } - - if (implicitly_started && (tk == token::div_end)) - throw std::invalid_argument("nana.place: the div-text ends prematurely at " + std::to_string(tknizer.pos())); - - field_gather * attached_field = nullptr; - - //find the field with specified name. - //the field may not be created. - auto i = fields.find(name); - if (fields.end() != i) - { - attached_field = i->second; - //the field is attached to a division, it means there is another division with same name. - if (attached_field->attached) - { - //The fields are allowed to have a same name. E.g. - //place.div("A "); - //place.modify("A", ""); Here the same name B must be allowed, otherwise it throws runtime error. - - bool allow_same_name = false; - if (!ignore_duplicate.empty()) - { - auto f = attached_field->attached->div_owner; - while (f) - { - if (f->name == ignore_duplicate) + case token::number: + margin.push(tknizer.number(), true); + break; + case token::array: + margin.set_array(tknizer.array()); + break; + case token::reparray: + for (std::size_t i = 0; i < 4; ++i) { - allow_same_name = true; - break; + auto n = tknizer.reparray().at(i); + if (n.empty()) n.assign(0); + margin.push(n); } - - f = f->div_owner; + break; + default: + break; } + break; + case token::identifier: + name = tknizer.idstr(); + break; + case token::left: + div_dir = ::nana::direction::west; break; + case token::right: + div_dir = ::nana::direction::east; break; + case token::top: + div_dir = ::nana::direction::north; break; + case token::bottom: + div_dir = ::nana::direction::south; break; + case token::undisplayed: + undisplayed = true; break; + case token::invisible: + invisible = true; break; + default: break; } - - if (!allow_same_name) - throw std::runtime_error("place, the name '" + name + "' is redefined."); - } - } - - token unmatch = token::width; - switch (div_type) - { - case token::eof: // "horizontal" div - case token::vert: // "vertical" div - if(token::eof == div_type) - unmatch = token::height; - - for (auto& ch : children) - if (ch->weigth_type == unmatch) - throw std::invalid_argument("nana.place: unmatch vertical-height/horizontal-width between division '" - +name+"' and children division '" + ch->name); - - div.reset(new div_arrange(token::vert == div_type, std::move(name), std::move(arrange))); - break; - case token::grid: - { - std::unique_ptr p(new div_grid(std::move(name), std::move(arrange), std::move(collapses))); - - if (array.size()) - p->dimension.first = array[0].integer(); - - if (array.size() > 1) - p->dimension.second = array[1].integer(); - - if (0 == p->dimension.first) - p->dimension.first = 1; - - if (0 == p->dimension.second) - p->dimension.second = 1; - - p->revise_collapses(); - div = std::move(p); - } - break; - case token::dock: - div.reset(new div_dock(std::move(name), this)); - break; - case token::switchable: - div.reset(new div_switchable(std::move(name), this)); - break; - default: - throw std::invalid_argument("nana.place: invalid division type."); - } - div->weigth_type = weight_type; - - //Requirements for min/max - //1, min and max != negative - //2, max >= min - if (min_px.is_negative()) min_px.reset(); - if (max_px.is_negative()) max_px.reset(); - if ((!min_px.empty()) && (!max_px.empty()) && (min_px.get_value(100) > max_px.get_value(100))) - { - min_px.reset(); - max_px.reset(); - } - - if (!min_px.empty()) - div->min_px = min_px; - - if (!max_px.empty()) - div->max_px = max_px; - - if ((!min_px.empty()) && (!weight.empty()) && (weight.get_value(100) < min_px.get_value(100))) - weight.reset(); - - if ((!max_px.empty()) && (!weight.empty()) && (weight.get_value(100) > max_px.get_value(100))) - weight.reset(); - - if (!weight.empty()) - div->weight = weight; - - div->gap = std::move(gap); - - //attach the field to the division - div->field = attached_field; - if (attached_field) - { - //Replaces the previous div with the new div which is allowed to have a same name. - - //Detaches the field from the previous div. - if (attached_field->attached) - attached_field->attached->field = nullptr; - - //Attaches new div - attached_field->attached = div.get(); - } - - if (children.size()) - { - if (division::kind::splitter == children.back()->kind_of_division) - { - children.pop_back(); - if (children.size()) - children.back()->div_next = nullptr; } - for (auto& child : children) - { - child->div_owner = div.get(); - if (division::kind::splitter == child->kind_of_division) - dynamic_cast(*child).direction(div_type != token::vert); - } + if (implicitly_started && (tk == token::div_end)) + throw std::invalid_argument("the div-text ends prematurely"); - if (token::dock == div_type) + field_gather * attached_field = nullptr; + + //find the field with specified name. + //the field may not be created. + auto i = fields.find(name); + if (fields.end() != i) { - //adjust these children for dock division - std::vector> adjusted_children; - for (auto & child : children) + attached_field = i->second; + //the field is attached to a division, it means there is another division with same name. + if (attached_field->attached) { - auto dockpn = new div_dockpane(std::move(child->name), this, child->dir); - dockpn->div_owner = child->div_owner; - dockpn->weight = child->weight; - adjusted_children.emplace_back(std::unique_ptr{ dockpn }); - } + //The fields are allowed to have a same name. E.g. + //place.div("A "); + //place.modify("A", ""); Here the same name B must be allowed, otherwise it throws std::invalid_argument. - division * next = nullptr; - for (int i = static_cast(adjusted_children.size()) - 1; i >= 0; --i) - { - adjusted_children[i]->div_next = next; - next = adjusted_children[i].get(); - } + bool allow_same_name = false; + if (!ignore_duplicate.empty()) + { + auto f = attached_field->attached->div_owner; + while (f) + { + if (f->name == ignore_duplicate) + { + allow_same_name = true; + break; + } - children.swap(adjusted_children); + f = f->div_owner; + } + } + + if (!allow_same_name) + throw std::invalid_argument("redefined field name"); + } } + + token unmatch = token::width; + switch (div_type) + { + case token::eof: // "horizontal" div + case token::vert: // "vertical" div + if (token::eof == div_type) + unmatch = token::height; + + for (auto& ch : children) + if (ch->weigth_type == unmatch) + throw std::invalid_argument("unmatch vertical-height/horizontal-width between division '" + + name + "' and children division '" + ch->name); + + div.reset(new div_arrange(token::vert == div_type, std::move(name), std::move(arrange))); + break; + case token::grid: + { + std::unique_ptr p(new div_grid(std::move(name), std::move(arrange), std::move(collapses))); + + if (array.size()) + p->dimension.first = array[0].integer(); + + if (array.size() > 1) + p->dimension.second = array[1].integer(); + + if (0 == p->dimension.first) + p->dimension.first = 1; + + if (0 == p->dimension.second) + p->dimension.second = 1; + + p->revise_collapses(); + div = std::move(p); + } + break; + case token::dock: + div.reset(new div_dock(std::move(name), this)); + break; + case token::switchable: + div.reset(new div_switchable(std::move(name), this)); + break; + default: + throw std::invalid_argument("invalid division type."); + } + div->weigth_type = weight_type; + + //Requirements for min/max + //1, min and max != negative + //2, max >= min + if (min_px.is_negative()) min_px.reset(); + if (max_px.is_negative()) max_px.reset(); + if ((!min_px.empty()) && (!max_px.empty()) && (min_px.get_value(100) > max_px.get_value(100))) + { + min_px.reset(); + max_px.reset(); + } + + if (!min_px.empty()) + div->min_px = min_px; + + if (!max_px.empty()) + div->max_px = max_px; + + if ((!min_px.empty()) && (!weight.empty()) && (weight.get_value(100) < min_px.get_value(100))) + weight.reset(); + + if ((!max_px.empty()) && (!weight.empty()) && (weight.get_value(100) > max_px.get_value(100))) + weight.reset(); + + if (!weight.empty()) + div->weight = weight; + + div->gap = std::move(gap); + + //attach the field to the division + div->field = attached_field; + if (attached_field) + { + //Replaces the previous div with the new div which is allowed to have a same name. + + //Detaches the field from the previous div. + if (attached_field->attached) + attached_field->attached->field = nullptr; + + //Attaches new div + attached_field->attached = div.get(); + } + + if (children.size()) + { + if (division::kind::splitter == children.back()->kind_of_division) + { + children.pop_back(); + if (children.size()) + children.back()->div_next = nullptr; + } + + for (auto& child : children) + { + child->div_owner = div.get(); + if (division::kind::splitter == child->kind_of_division) + dynamic_cast(*child).direction(div_type != token::vert); + } + + if (token::dock == div_type) + { + //adjust these children for dock division + std::vector> adjusted_children; + for (auto& child : children) + { + auto dockpn = new div_dockpane(std::move(child->name), this, child->dir); + dockpn->div_owner = child->div_owner; + dockpn->weight = child->weight; + adjusted_children.emplace_back(std::unique_ptr{ dockpn }); + } + + division* next = nullptr; + for (int i = static_cast(adjusted_children.size()) - 1; i >= 0; --i) + { + adjusted_children[i]->div_next = next; + next = adjusted_children[i].get(); + } + + children.swap(adjusted_children); + } + } + + div->children.swap(children); + div->margin = std::move(margin); + div->dir = div_dir; + + div->display = !undisplayed; + div->visible = !(undisplayed || invisible); + div->fit = fit; + div->fit_parameters = std::move(fit_parameters); + } + catch (place::error& ) { throw; } + catch ( error& ) { throw; } + catch (place_parts::tokenizer::error& e) + { + throw error(e.what(), name, e.pos); + } + catch (std::invalid_argument& e) + { + throw error(e.what(), name, tknizer.pos()); + } + catch (std::exception& e) + { + throw error(std::string{"unexpected error: "}+e.what(), name, tknizer.pos()); + } + catch (...) + { + throw error("unknow error", name, tknizer.pos()); } - - div->children.swap(children); - div->margin = std::move(margin); - div->dir = div_dir; - - div->display = !undisplayed; - div->visible = !(undisplayed || invisible); - div->fit = fit; - div->fit_parameters = std::move(fit_parameters); - return div; } @@ -3213,7 +3260,8 @@ namespace nana void place::bind(window wd) { if (impl_->window_handle) - throw std::runtime_error("place.bind: it has already bound to a window."); + throw error(" bind('"+ API::window_caption(wd).substr(0, 80) + + "'): it was already bound to another window.", *this); impl_->window_handle = wd; impl_->event_size_handle = API::events(wd).resized.connect_unignorable([this](const arg_resized& arg) @@ -3242,20 +3290,36 @@ namespace nana void place::div(std::string div_text) { - place_parts::tokenizer tknizer(div_text.c_str()); - impl_->disconnect(); - auto div = impl_->scan_div(tknizer, true); try { + place_parts::tokenizer tknizer(div_text.c_str()); + impl_->disconnect(); + auto div = impl_->scan_div(tknizer, true); impl_->connect(div.get()); //throws if there is a redefined name of field. impl_->root_division.reset(); //clear attachments div-fields impl_->root_division.swap(div); impl_->div_text.swap(div_text); } + catch (place::error & ) { throw; } + catch (place::implement::error & e) + { + throw error("failed to set div('" + div_text + "'): " + e.what(), *this, e.field, e.pos); + } + catch (place_parts::tokenizer::error & e) + { + throw error("failed to set div('" + div_text + "'): " + e.what(), *this, "", e.pos); + } + catch (std::invalid_argument & e) + { + throw error("failed to set div('" + div_text + "'): " + e.what(), *this); + } + catch (std::exception & e) + { + throw error("failed to set div('"+div_text+"'): unexpected error: " +e.what(), *this ); + } catch (...) { - //redefined a name of field - throw; + throw error("failed to set div('" + div_text + "'): unknonw error", *this); } } @@ -3272,23 +3336,15 @@ namespace nana void place::modify(const char* name, const char* div_text) { if (nullptr == div_text) - throw std::invalid_argument("nana.place: invalid div-text"); + throw error("modify(): invalid div-text (=nullptr)", *this); - if (nullptr == name) name = ""; - - //check the name, it throws std::invalid_argument - //if name violate the naming convention. - place_parts::check_field_name(name); + if (! valid_field_name(name) ) + throw badname("modify()", *this, name); auto div_ptr = impl_->search_div_name(impl_->root_division.get(), name); if (!div_ptr) - { - std::string what = "nana::place: field '"; - what += name; - what += "' is not found."; - - throw std::invalid_argument(what); - } + throw error("modify(): field was not found", *this, name); + std::unique_ptr* replaced = nullptr; @@ -3342,23 +3398,44 @@ namespace nana } } } + catch (place::error & ) + { + replaced->swap(impl_->tmp_replaced); + throw; + } + catch (place::implement::error & e) + { + replaced->swap(impl_->tmp_replaced); + throw error("failed to modify('"+std::string(name) +", "+ div_text + "'): " + e.what(), *this, e.field, e.pos); + } + catch (place_parts::tokenizer::error & e) + { + replaced->swap(impl_->tmp_replaced); + throw error("failed to modify('" + std::string(name) + ", " + div_text + "'): " + e.what(), *this, "", e.pos); + } + catch (std::invalid_argument & e) + { + replaced->swap(impl_->tmp_replaced); + throw error("failed to modify('" + std::string(name) + ", " + div_text + "'): " + e.what(), *this); + } + catch (std::exception & e) + { + replaced->swap(impl_->tmp_replaced); + throw error("failed to modify('" + std::string(name) + ", " + div_text + "'): unexpected error: " + e.what(), *this); + } catch (...) { replaced->swap(impl_->tmp_replaced); - throw; + throw error("failed to modify('" + std::string(name) + ", " + div_text + "'): unknonw error", *this); } } place::field_reference place::field(const char* name) { - if (nullptr == name) - name = ""; + if (!valid_field_name(name)) + throw badname("field()", *this, name); - //check the name, it throws std::invalid_argument - //if name violate the naming convention. - place_parts::check_field_name(name); - - //get the field with specified name, if no such field with specified name + //get the field with the specified name. If no such field with specified name //then create one. auto & p = impl_->fields[name]; if (nullptr == p) @@ -3367,12 +3444,12 @@ namespace nana if ((!p->attached) && impl_->root_division) { //search the division with the specified name, - //and attached the division to the field + //and attaches the division to the field auto div = implement::search_div_name(impl_->root_division.get(), name); if (div) { if (div->field && (div->field != p)) - throw std::runtime_error("nana.place: unexpected error, the division attaches an unexpected field."); + throw error("field(): the division attaches an unexpected field", *this, name); div->field = p; p->attached = div; @@ -3449,10 +3526,8 @@ namespace nana void place::field_visible(const char* name, bool vsb) { - if (!name) name = ""; - - //May throw std::invalid_argument - place_parts::check_field_name(name); + if (!valid_field_name(name)) + throw badname("field_visible()", *this, name); auto div = impl_->search_div_name(impl_->root_division.get(), name); if (div) @@ -3464,10 +3539,8 @@ namespace nana bool place::field_visible(const char* name) const { - if (!name) name = ""; - - //May throw std::invalid_argument - place_parts::check_field_name(name); + if (!valid_field_name(name)) + throw badname("field_visible()", *this, name); auto div = impl_->search_div_name(impl_->root_division.get(), name); return (div && div->visible); @@ -3475,10 +3548,8 @@ namespace nana void place::field_display(const char* name, bool dsp) { - if (!name) name = ""; - - //May throw std::invalid_argument - place_parts::check_field_name(name); + if (!valid_field_name(name)) + throw badname("field_display()", *this, name); auto div = impl_->search_div_name(impl_->root_division.get(), name); if (div) @@ -3491,10 +3562,8 @@ namespace nana bool place::field_display(const char* name) const { - if (!name) name = ""; - - //May throw std::invalid_argument - place_parts::check_field_name(name); + if (!valid_field_name(name)) + throw badname("field_display()", *this, name); auto div = impl_->search_div_name(impl_->root_division.get(), name); return (div && div->display); @@ -3531,9 +3600,8 @@ namespace nana place& place::dock(const std::string& name, std::string factory_name, std::function(window)> factory) { - //check the name, it throws std::invalid_argument - //if name violate the naming convention. - place_parts::check_field_name(name.data()); + if (!valid_field_name(name.data())) + throw badname("dock()", *this, name.data()); auto & dock_ptr = impl_->docks[name]; if (!dock_ptr) @@ -3543,7 +3611,7 @@ namespace nana if (!factory_name.empty()) { if (impl_->dock_factoris.find(factory_name) != impl_->dock_factoris.end()) - throw std::invalid_argument("nana::place - the specified factory name(" + factory_name + ") already exists"); + throw error("dock() - the specified factory name(" + factory_name + ") already exists", *this, name); impl_->dock_factoris[factory_name] = dock_ptr; dock_ptr->factories[factory_name] = std::move(factory); @@ -3579,7 +3647,7 @@ namespace nana { auto i = impl_->dock_factoris.find(factory); if (i == impl_->dock_factoris.end()) - throw std::invalid_argument("invalid factory name(" + factory + ") of dockpane"); + throw std::invalid_argument("nana::place::dock_create - invalid factory name(" + factory + ") of dockpane"); auto dock_ptr = i->second; if (dock_ptr->attached) @@ -3600,7 +3668,27 @@ namespace nana return nullptr; } + + place::error::error(const std::string& what, + const place& plc, + std::string field, + std::string::size_type pos) + + : std::invalid_argument( "from widget '" + + API::window_caption(plc.window_handle()).substr(0,80) + + "'; nana::place error " + + what + + "' in field '" + field + + (pos == std::string::npos ? "' " : "' at position " + std::to_string(pos)) + + " in div_text:\n" + plc.div() ), + base_what( what ), + owner_caption( API::window_caption(plc.window_handle()).substr(0,80) ), + div_text( plc.div() ), + field( field ), + pos( pos ) + {} //end class place + }//end namespace nana #include diff --git a/source/gui/place_parts.hpp b/source/gui/place_parts.hpp index 2105dd02..85ef8dfe 100644 --- a/source/gui/place_parts.hpp +++ b/source/gui/place_parts.hpp @@ -1,13 +1,13 @@ -/* +/** * Parts of Class Place * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at + * (See accompanying file LICENSE or copy at * http://www.boost.org/LICENSE_1_0.txt) * - * @file: nana/gui/place_parts.hpp + * @file nana/gui/place_parts.hpp */ #ifndef NANA_GUI_PLACE_PARTS_HPP #define NANA_GUI_PLACE_PARTS_HPP @@ -126,6 +126,12 @@ namespace nana { close_fn_ = std::move(fn); } + + bool hit_close() const + { + return x_pointed_; + } + private: virtual void attached(widget_reference wdg, graph_reference graph) override { @@ -140,7 +146,7 @@ namespace nana //draw caption auto text = to_wstring(API::window_caption(window_handle_)); - text_rd_->render({ 3, 1 }, text.data(), text.size(), graph.size().width - 20, true); + text_rd_->render({ 3, 1 }, text.data(), text.size(), graph.size().width - 20, paint::text_renderer::mode::truncate_with_ellipsis); //draw x button auto r = _m_button_area(); @@ -310,7 +316,9 @@ namespace nana { move_pos += moves_.start_container_pos; API::move_window(container_->handle(), move_pos); - notifier_->notify_move(); + + if(!caption_.get_drawer_trigger().hit_close()) + notifier_->notify_move(); } } } diff --git a/source/gui/programming_interface.cpp b/source/gui/programming_interface.cpp index a73ec1bf..2610e17e 100644 --- a/source/gui/programming_interface.cpp +++ b/source/gui/programming_interface.cpp @@ -11,9 +11,9 @@ * @author: Jinhao */ +#include "detail/basic_window.hpp" #include #include -#include #include #include #include @@ -81,104 +81,100 @@ namespace API bool emit_event(event_code evt_code, window wd, const ::nana::event_arg& arg) { - return restrict::bedrock.emit(evt_code, reinterpret_cast<::nana::detail::basic_window*>(wd), arg, true, restrict::bedrock.get_thread_context(), false); + return restrict::bedrock.emit(evt_code, wd, arg, true, restrict::bedrock.get_thread_context(), false); } bool emit_internal_event(event_code evt_code, window wd, const ::nana::event_arg& arg) { - return restrict::bedrock.emit(evt_code, reinterpret_cast<::nana::detail::basic_window*>(wd), arg, true, restrict::bedrock.get_thread_context(), true); + return restrict::bedrock.emit(evt_code, wd, arg, true, restrict::bedrock.get_thread_context(), true); } void enum_widgets_function_base::enum_widgets(window wd, bool recursive) { - auto iwd = reinterpret_cast(wd); - internal_scope_guard lock; - if (restrict::wd_manager().available(iwd)) + if (is_window(wd)) { - //Use a copy, because enum function may close a child window and the original children container would be changed, - //in the situation, the walking thorugh directly to the iwd->children would cause error. - auto children = iwd->children; + //Use a copy, because enumerating function may close a child window and the original children container would be changed, + //in the situation, the walking thorugh directly to the wd->children would cause error. + auto children = wd->children; for (auto child : children) { - auto widget_ptr = API::get_widget(reinterpret_cast(child)); + auto widget_ptr = API::get_widget(child); if (!widget_ptr) continue; _m_enum_fn(widget_ptr); if (recursive) - enum_widgets(reinterpret_cast(child), recursive); + enum_widgets(child, recursive); } } } general_events* get_general_events(window wd) { - if (!restrict::wd_manager().available(reinterpret_cast(wd))) + if (empty_window(wd)) return nullptr; - return reinterpret_cast(wd)->annex.events_ptr.get(); + return wd->annex.events_ptr.get(); } }//end namespace detail void effects_edge_nimbus(window wd, effects::edge_nimbus en) { - auto const iwd = reinterpret_cast(wd); - internal_scope_guard isg; - if(restrict::wd_manager().available(iwd)) + internal_scope_guard lock; + if(is_window(wd)) { - auto & cont = iwd->root_widget->other.attribute.root->effects_edge_nimbus; + auto & cont = wd->root_widget->other.attribute.root->effects_edge_nimbus; if(effects::edge_nimbus::none != en) { - if (iwd->effect.edge_nimbus == effects::edge_nimbus::none) + if (wd->effect.edge_nimbus == effects::edge_nimbus::none) { - cont.emplace_back(basic_window::edge_nimbus_action{ iwd, false}); + cont.emplace_back(basic_window::edge_nimbus_action{ wd, false}); } - iwd->effect.edge_nimbus = static_cast(static_cast(en) | static_cast(iwd->effect.edge_nimbus)); + wd->effect.edge_nimbus = static_cast(static_cast(en) | static_cast(wd->effect.edge_nimbus)); } else { - if(effects::edge_nimbus::none != iwd->effect.edge_nimbus) + if(effects::edge_nimbus::none != wd->effect.edge_nimbus) { for(auto i = cont.begin(); i != cont.end(); ++i) - if(i->window == iwd) + if(i->window == wd) { cont.erase(i); break; } } - iwd->effect.edge_nimbus = effects::edge_nimbus::none; + wd->effect.edge_nimbus = effects::edge_nimbus::none; } } } effects::edge_nimbus effects_edge_nimbus(window wd) { - auto const iwd = reinterpret_cast(wd); - internal_scope_guard isg; - return (restrict::wd_manager().available(iwd) ? iwd->effect.edge_nimbus : effects::edge_nimbus::none); + internal_scope_guard lock; + return (is_window(wd) ? wd->effect.edge_nimbus : effects::edge_nimbus::none); } void effects_bground(window wd, const effects::bground_factory_interface& factory, double fade_rate) { if (fade_rate < 0.0 || fade_rate > 1.0) throw std::invalid_argument("effects_bground: value range of fade_rate must be [0, 1]."); - auto const iwd = reinterpret_cast(wd); - internal_scope_guard isg; - if(restrict::wd_manager().available(iwd)) + + internal_scope_guard lock; + if(is_window(wd)) { auto new_effect_ptr = effects::effects_accessor::create(factory); if(nullptr == new_effect_ptr) return; - delete iwd->effect.bground; - iwd->effect.bground = new_effect_ptr; - iwd->effect.bground_fade_rate = fade_rate; - restrict::wd_manager().enable_effects_bground(iwd, true); + delete wd->effect.bground; + wd->effect.bground = new_effect_ptr; + wd->effect.bground_fade_rate = fade_rate; + restrict::wd_manager().enable_effects_bground(wd, true); if (fade_rate < 0.01) - iwd->flags.make_bground_declared = true; + wd->flags.make_bground_declared = true; API::refresh_window(wd); } @@ -192,21 +188,19 @@ namespace API bground_mode effects_bground_mode(window wd) { - auto const iwd = reinterpret_cast(wd); - internal_scope_guard isg; - if(restrict::wd_manager().available(iwd) && iwd->effect.bground) - return (iwd->effect.bground_fade_rate <= 0.009 ? bground_mode::basic : bground_mode::blend); + internal_scope_guard lock; + if(is_window(wd) && wd->effect.bground) + return (wd->effect.bground_fade_rate <= 0.009 ? bground_mode::basic : bground_mode::blend); return bground_mode::none; } void effects_bground_remove(window wd) { - const auto iwd = reinterpret_cast(wd); - internal_scope_guard isg; - if(restrict::wd_manager().available(iwd)) + internal_scope_guard lock; + if(is_window(wd)) { - if(restrict::wd_manager().enable_effects_bground(iwd, false)) + if(restrict::wd_manager().enable_effects_bground(wd, false)) API::refresh_window(wd); } } @@ -221,99 +215,92 @@ namespace API bool set_events(window wd, const std::shared_ptr& gep) { - auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - if (restrict::wd_manager().available(iwd)) - iwd->set_events(gep); + if (is_window(wd)) + return wd->set_events(gep); return false; } void set_scheme(window wd, widget_geometrics* wdg_geom) { - auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - if (restrict::wd_manager().available(iwd)) - iwd->annex.scheme = wdg_geom; + if (is_window(wd)) + wd->annex.scheme = wdg_geom; } widget_geometrics* get_scheme(window wd) { - auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - return (restrict::wd_manager().available(iwd) ? iwd->annex.scheme : nullptr); + return (is_window(wd) ? wd->annex.scheme : nullptr); } void set_measurer(window wd, ::nana::dev::widget_content_measurer_interface* measurer) { - auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - if (restrict::wd_manager().available(iwd)) - iwd->annex.content_measurer = measurer; + if (is_window(wd)) + wd->annex.content_measurer = measurer; } - void attach_drawer(widget& wd, drawer_trigger& dr) + void attach_drawer(widget& wdg, drawer_trigger& dr) { - const auto iwd = reinterpret_cast(wd.handle()); - internal_scope_guard isg; - if(restrict::wd_manager().available(iwd)) + const auto wd = wdg.handle(); + internal_scope_guard lock; + if(is_window(wd)) { - iwd->drawer.graphics.make(iwd->dimension); - iwd->drawer.graphics.rectangle(true, iwd->annex.scheme->background.get_color()); - iwd->drawer.attached(wd, dr); - iwd->drawer.refresh(); //Always redraw no matter it is visible or invisible. This can make the graphics data correctly. + wd->drawer.graphics.make(wd->dimension); + wd->drawer.graphics.rectangle(true, wd->annex.scheme->background.get_color()); + wd->drawer.attached(wdg, dr); + wd->drawer.refresh(); //Always redraw no matter it is visible or invisible. This can make the graphics data correctly. } } ::nana::detail::native_string_type window_caption(window wd) noexcept { - auto const iwd = reinterpret_cast(wd); - internal_scope_guard isg; - - if(restrict::wd_manager().available(iwd)) + internal_scope_guard lock; + if(is_window(wd)) { - if (category::flags::root == iwd->other.category) - return interface_type::window_caption(iwd->root); - return iwd->title; + if (category::flags::root == wd->other.category) + return interface_type::window_caption(wd->root); + return wd->title; } return {}; } void window_caption(window wd, ::nana::detail::native_string_type title) { - auto const iwd = reinterpret_cast(wd); internal_scope_guard lock; - if (restrict::wd_manager().available(iwd)) + if (is_window(wd)) { - iwd->title.swap(title); - if (iwd->other.category == category::flags::root) - interface_type::window_caption(iwd->root, iwd->title); + wd->title.swap(title); + if (wd->other.category == category::flags::root) + interface_type::window_caption(wd->root, wd->title); - restrict::wd_manager().update(iwd, true, false); + refresh_window(wd); } } window create_window(window owner, bool nested, const rectangle& r, const appearance& ap, widget* wdg) { - return reinterpret_cast(restrict::wd_manager().create_root(reinterpret_cast(owner), nested, r, ap, wdg)); + return restrict::wd_manager().create_root(owner, nested, r, ap, wdg); } window create_widget(window parent, const rectangle& r, widget* wdg) { - return reinterpret_cast(restrict::wd_manager().create_widget(reinterpret_cast(parent), r, false, wdg)); + return restrict::wd_manager().create_widget(parent, r, false, wdg); } window create_lite_widget(window parent, const rectangle& r, widget* wdg) { - return reinterpret_cast(restrict::wd_manager().create_widget(reinterpret_cast(parent), r, true, wdg)); + return restrict::wd_manager().create_widget(parent, r, true, wdg); } paint::graphics* window_graphics(window wd) { - internal_scope_guard isg; - if(restrict::wd_manager().available(reinterpret_cast(wd))) - return &reinterpret_cast(wd)->drawer.graphics; + internal_scope_guard lock; + if(is_window(wd)) + return &(wd->drawer.graphics); return nullptr; } @@ -325,25 +312,24 @@ namespace API void register_menu_window(window wd, bool has_keyboard) { internal_scope_guard lock; - if (restrict::wd_manager().available(reinterpret_cast(wd))) - restrict::bedrock.set_menu(reinterpret_cast(wd)->root, has_keyboard); + if (is_window(wd)) + restrict::bedrock.set_menu(wd->root, has_keyboard); } void set_menubar(window wd, bool attach) { - auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - if (restrict::wd_manager().available(iwd)) + if (is_window(wd)) { - auto root_attr = iwd->root_widget->other.attribute.root; + auto root_attr = wd->root_widget->other.attribute.root; if (attach) { if (!root_attr->menubar) - root_attr->menubar = iwd; + root_attr->menubar = wd; } else { - if (iwd == root_attr->menubar) + if (wd == root_attr->menubar) root_attr->menubar = nullptr; } } @@ -351,35 +337,30 @@ namespace API void enable_space_click(window wd, bool enable) { - auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - if (restrict::wd_manager().available(iwd)) - iwd->flags.space_click_enabled = enable; + if (is_window(wd)) + wd->flags.space_click_enabled = enable; } bool copy_transparent_background(window wd, paint::graphics& graph) { - auto & buf = reinterpret_cast(wd)->other.glass_buffer; internal_scope_guard lock; if (bground_mode::basic != API::effects_bground_mode(wd)) return false; - buf.paste(rectangle{ buf.size() }, graph, 0, 0); - + wd->other.glass_buffer.paste(rectangle{ wd->other.glass_buffer.size() }, graph, 0, 0); return true; } bool copy_transparent_background(window wd, const rectangle& src_r, paint::graphics& graph, const point& dst_pt) { - auto iwd = reinterpret_cast(wd); internal_scope_guard lock; if (bground_mode::basic != API::effects_bground_mode(wd)) return false; - iwd->other.glass_buffer.paste(src_r, graph, dst_pt.x, dst_pt.y); - + wd->other.glass_buffer.paste(src_r, graph, dst_pt.x, dst_pt.y); return true; } @@ -413,18 +394,16 @@ namespace API void window_draggable(window wd, bool enabled) { - auto real_wd = reinterpret_cast(wd); internal_scope_guard lock; - if (restrict::wd_manager().available(real_wd)) - real_wd->flags.draggable = enabled; + if (is_window(wd)) + wd->flags.draggable = enabled; } bool window_draggable(window wd) { - auto real_wd = reinterpret_cast(wd); internal_scope_guard lock; - if (restrict::wd_manager().available(real_wd)) - return real_wd->flags.draggable; + if (is_window(wd)) + return wd->flags.draggable; return false; } @@ -435,12 +414,17 @@ namespace API widget* get_widget(window wd) { internal_scope_guard lock; - if (restrict::wd_manager().available(reinterpret_cast(wd))) - return reinterpret_cast(wd)->widget_notifier->widget_ptr(); + if (is_window(wd)) + return wd->widget_notifier->widget_ptr(); return nullptr; } + void font_languages(const std::string& langs) + { + ::nana::platform_abstraction::font_languages(langs); + } + //close all windows in current thread void exit() { @@ -490,15 +474,15 @@ namespace API bool register_shortkey(window wd, unsigned long key) { - return restrict::wd_manager().register_shortkey(reinterpret_cast(wd), key); + return restrict::wd_manager().register_shortkey(wd, key); } void unregister_shortkey(window wd) { - restrict::wd_manager().unregister_shortkey(reinterpret_cast(wd), false); + restrict::wd_manager().unregister_shortkey(wd, false); } - ::nana::point cursor_position() + ::nana::point cursor_position() { return interface_type::cursor_position(); } @@ -531,37 +515,35 @@ namespace API void window_icon(window wd, const paint::image& small_icon, const paint::image& big_icon) { if(nullptr != wd) - restrict::wd_manager().icon(reinterpret_cast(wd), small_icon, big_icon); + restrict::wd_manager().icon(wd, small_icon, big_icon); } bool empty_window(window wd) { - return (restrict::wd_manager().available(reinterpret_cast(wd)) == false); + return (restrict::wd_manager().available(wd) == false); } bool is_window(window wd) { - return restrict::wd_manager().available(reinterpret_cast(wd)); + return restrict::wd_manager().available(wd); } bool is_destroying(window wd) { - auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - if (!restrict::wd_manager().available(iwd)) + if (!restrict::wd_manager().available(wd)) return false; - return iwd->flags.destroying; + return wd->flags.destroying; } void enable_dropfiles(window wd, bool enb) { internal_scope_guard lock; - auto iwd = reinterpret_cast(wd); auto native_handle = API::root(wd); if (native_handle) { - iwd->flags.dropable = enb; + wd->flags.dropable = enb; interface_type::enable_dropfiles(native_handle, enb); } } @@ -575,88 +557,83 @@ namespace API { internal_scope_guard lock; if(is_window(wd)) - return reinterpret_cast(wd)->root; + return wd->root; return nullptr; } window root(native_window_type wd) { - return reinterpret_cast(restrict::wd_manager().root(wd)); + return restrict::wd_manager().root(wd); } void enable_double_click(window wd, bool dbl) { - auto const iwd = reinterpret_cast(wd); internal_scope_guard lock; - if(restrict::wd_manager().available(iwd)) - iwd->flags.dbl_click = dbl; + if(is_window(wd)) + wd->flags.dbl_click = dbl; } void fullscreen(window wd, bool v) { internal_scope_guard lock; - if(restrict::wd_manager().available(reinterpret_cast(wd))) - reinterpret_cast(wd)->flags.fullscreen = v; + if(is_window(wd)) + wd->flags.fullscreen = v; } void close_window(window wd) { - restrict::wd_manager().close(reinterpret_cast(wd)); + restrict::wd_manager().close(wd); } void show_window(window wd, bool show) { - restrict::wd_manager().show(reinterpret_cast(wd), show); + restrict::wd_manager().show(wd, show); } bool visible(window wd) { - auto const iwd = reinterpret_cast(wd); internal_scope_guard lock; - if(restrict::wd_manager().available(iwd)) + if(is_window(wd)) { - if(iwd->other.category == category::flags::root) - return interface_type::is_window_visible(iwd->root); - return iwd->visible; + if(wd->other.category == category::flags::root) + return interface_type::is_window_visible(wd->root); + return wd->visible; } return false; } void restore_window(window wd) { - auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - if(restrict::wd_manager().available(iwd)) + if(is_window(wd)) { - if(iwd->other.category == category::flags::root) - interface_type::restore_window(iwd->root); + if(wd->other.category == category::flags::root) + interface_type::restore_window(wd->root); } } void zoom_window(window wd, bool ask_for_max) { - auto core_wd = reinterpret_cast(wd); internal_scope_guard lock; - if(restrict::wd_manager().available(core_wd)) + if(is_window(wd)) { - if(category::flags::root == core_wd->other.category) - interface_type::zoom_window(core_wd->root, ask_for_max); + if(category::flags::root == wd->other.category) + interface_type::zoom_window(wd->root, ask_for_max); } } window get_parent_window(window wd) { - auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - if (restrict::wd_manager().available(iwd)) + if (is_window(wd)) { - if (category::flags::root == iwd->other.category) + if (category::flags::root == wd->other.category) { - return reinterpret_cast(restrict::wd_manager().root( - interface_type::get_window(iwd->root, window_relationship::parent) - )); + return restrict::wd_manager().root( + interface_type::get_window(wd->root, window_relationship::parent) + ); } - return reinterpret_cast(iwd->parent); + return wd->parent; } return nullptr; @@ -664,13 +641,12 @@ namespace API window get_owner_window(window wd) { - auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - if(restrict::wd_manager().available(iwd) && (iwd->other.category == category::flags::root)) + if(is_window(wd) && (wd->other.category == category::flags::root)) { - auto owner = interface_type::get_window(iwd->root, window_relationship::owner); + auto owner = interface_type::get_window(wd->root, window_relationship::owner); if(owner) - return reinterpret_cast(restrict::wd_manager().root(owner)); + return restrict::wd_manager().root(owner); } return nullptr; @@ -678,7 +654,7 @@ namespace API bool set_parent_window(window wd, window new_parent) { - return restrict::wd_manager().set_parent(reinterpret_cast(wd), reinterpret_cast(new_parent)); + return restrict::wd_manager().set_parent(wd, new_parent); } void umake_event(event_handle eh) @@ -688,32 +664,30 @@ namespace API nana::point window_position(window wd) { - auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - if(restrict::wd_manager().available(iwd)) + if(is_window(wd)) { - return ( (iwd->other.category == category::flags::root) ? - interface_type::window_position(iwd->root) : iwd->pos_owner); + return ( (wd->other.category == category::flags::root) ? + interface_type::window_position(wd->root) : wd->pos_owner); } return nana::point{}; } void move_window(window wd, const point& pos) { - auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - if(restrict::wd_manager().move(iwd, pos.x, pos.y, false)) + if(restrict::wd_manager().move(wd, pos.x, pos.y, false)) { basic_window* update_wd = nullptr; - if (iwd->displayed() && iwd->effect.bground) + if (wd->displayed() && wd->effect.bground) { - update_wd = iwd; - restrict::wd_manager().update(iwd, true, false); + update_wd = wd; + refresh_window(wd); } - basic_window* anc = iwd; - if (category::flags::root != iwd->other.category) - anc = iwd->seek_non_lite_widget_ancestor(); + basic_window* anc = wd; + if (category::flags::root != wd->other.category) + anc = wd->seek_non_lite_widget_ancestor(); if (anc != update_wd) restrict::wd_manager().update(anc, false, false); @@ -722,14 +696,13 @@ namespace API void move_window(window wd, const rectangle& r) { - auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - if(restrict::wd_manager().move(iwd, r)) + if(restrict::wd_manager().move(wd, r)) { - if (category::flags::root != iwd->other.category) - iwd = iwd->seek_non_lite_widget_ancestor(); + if (category::flags::root != wd->other.category) + wd = wd->seek_non_lite_widget_ancestor(); - restrict::wd_manager().update(iwd, false, false); + restrict::wd_manager().update(wd, false, false); } } @@ -740,23 +713,21 @@ namespace API bool set_window_z_order(window wd, window wd_after, z_order_action action_if_no_wd_after) { - auto iwd = reinterpret_cast(wd); native_window_type native_after = nullptr; internal_scope_guard lock; - if (restrict::wd_manager().available(iwd) && (category::flags::root == iwd->other.category)) + if (is_window(wd) && (category::flags::root == wd->other.category)) { if(wd_after) { - auto iwd_after = reinterpret_cast(wd_after); - if (restrict::wd_manager().available(iwd_after) && (iwd_after->other.category == category::flags::root)) + if (is_window(wd_after) && (wd_after->other.category == category::flags::root)) { - native_after = iwd_after->root; + native_after = wd_after->root; action_if_no_wd_after = z_order_action::none; } else return false; } - interface_type::set_window_z_order(iwd->root, native_after, action_if_no_wd_after); + interface_type::set_window_z_order(wd->root, native_after, action_if_no_wd_after); return true; } return false; @@ -764,23 +735,21 @@ namespace API void draw_through(window wd, std::function draw_fn) { - auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - if (!restrict::wd_manager().available(iwd)) + if (empty_window(wd)) throw std::invalid_argument("draw_through: invalid window parameter"); - if (::nana::category::flags::root != iwd->other.category) + if (::nana::category::flags::root != wd->other.category) throw std::invalid_argument("draw_through: the window is not a root widget"); - iwd->other.attribute.root->draw_through.swap(draw_fn); + wd->other.attribute.root->draw_through.swap(draw_fn); } void map_through_widgets(window wd, native_drawable_type drawable) { - auto iwd = reinterpret_cast<::nana::detail::basic_window*>(wd); internal_scope_guard lock; - if (restrict::wd_manager().available(iwd) && iwd->is_draw_through() ) - restrict::bedrock.map_through_widgets(iwd, drawable); + if (is_window(wd) && wd->is_draw_through() ) + restrict::bedrock.map_through_widgets(wd, drawable); } nana::size window_size(window wd) @@ -792,29 +761,27 @@ namespace API void window_size(window wd, const size& sz) { - auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - if(restrict::wd_manager().size(iwd, sz, false, false)) + if(restrict::wd_manager().size(wd, sz, false, false)) { - if (category::flags::root != iwd->other.category) - iwd = iwd->seek_non_lite_widget_ancestor(); + if (category::flags::root != wd->other.category) + wd = wd->seek_non_lite_widget_ancestor(); - restrict::wd_manager().update(iwd, false, false); + restrict::wd_manager().update(wd, false, false); } } ::nana::size window_outline_size(window wd) { - auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - if (!restrict::wd_manager().available(iwd)) + if (empty_window(wd)) return{}; auto sz = window_size(wd); - if(category::flags::root == iwd->other.category) + if(category::flags::root == wd->other.category) { - auto fm_extents = interface_type::window_frame_extents(iwd->root); + auto fm_extents = interface_type::window_frame_extents(wd->root); sz.width += fm_extents.left + fm_extents.right; sz.height += fm_extents.top + fm_extents.bottom; } @@ -824,13 +791,12 @@ namespace API void window_outline_size(window wd, const size& sz) { - auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - if (restrict::wd_manager().available(iwd)) + if (is_window(wd)) { - if (category::flags::root == iwd->other.category) + if (category::flags::root == wd->other.category) { - auto fm_extents = interface_type::window_frame_extents(iwd->root); + auto fm_extents = interface_type::window_frame_extents(wd->root); size inner_size = sz; @@ -853,20 +819,18 @@ namespace API std::optional window_rectangle(window wd) { - auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - if (restrict::wd_manager().available(iwd)) - return rectangle(iwd->pos_owner, iwd->dimension); + if (is_window(wd)) + return rectangle(wd->pos_owner, wd->dimension); return{}; } bool get_window_rectangle(window wd, rectangle& r) { - auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - if(restrict::wd_manager().available(iwd)) + if(is_window(wd)) { - r = rectangle(iwd->pos_owner, iwd->dimension); + r = rectangle(wd->pos_owner, wd->dimension); return true; } return false; @@ -874,28 +838,27 @@ namespace API bool track_window_size(window wd, const nana::size& sz, bool true_for_max) { - auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - if(restrict::wd_manager().available(iwd) == false) + if(is_window(wd) == false) return false; - nana::size & ts = (true_for_max ? iwd->max_track_size : iwd->min_track_size); + nana::size & ts = (true_for_max ? wd->max_track_size : wd->min_track_size); if(!sz.empty()) { if(true_for_max) { //Make sure the new size is larger than min size - if (iwd->min_track_size.width > sz.width || iwd->min_track_size.height > sz.height) + if (wd->min_track_size.width > sz.width || wd->min_track_size.height > sz.height) return false; } else { //Make sure that the new size is less than max size - if ((iwd->max_track_size.width || iwd->max_track_size.height) && (iwd->max_track_size.width < sz.width || iwd->max_track_size.height < sz.height)) + if ((wd->max_track_size.width || wd->max_track_size.height) && (wd->max_track_size.width < sz.width || wd->max_track_size.height < sz.height)) return false; } - ts = interface_type::check_track_size(sz, iwd->extra_width, iwd->extra_height, true_for_max); + ts = interface_type::check_track_size(sz, wd->extra_width, wd->extra_height, true_for_max); } else ts.width = ts.height = 0; @@ -904,155 +867,145 @@ namespace API void window_enabled(window wd, bool enabled) { - auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - if(restrict::wd_manager().available(iwd) && (iwd->flags.enabled != enabled)) + if(is_window(wd) && (wd->flags.enabled != enabled)) { - iwd->flags.enabled = enabled; - restrict::wd_manager().update(iwd, true, true); - if(category::flags::root == iwd->other.category) - interface_type::enable_window(iwd->root, enabled); + wd->flags.enabled = enabled; + restrict::wd_manager().update(wd, true, true); + if(category::flags::root == wd->other.category) + interface_type::enable_window(wd->root, enabled); } } bool window_enabled(window wd) { - auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - return (restrict::wd_manager().available(iwd) ? iwd->flags.enabled : false); + return (is_window(wd) ? wd->flags.enabled : false); } //refresh_window //@brief: Refresh the window and display it immediately. void refresh_window(window wd) { - restrict::wd_manager().update(reinterpret_cast(wd), true, false); + restrict::wd_manager().update(wd, true, false); } void refresh_window_tree(window wd) { - restrict::wd_manager().refresh_tree(reinterpret_cast(wd)); + restrict::wd_manager().refresh_tree(wd); } //update_window //@brief: it displays a window immediately without refreshing. void update_window(window wd) { - restrict::wd_manager().update(reinterpret_cast(wd), false, true); + restrict::wd_manager().update(wd, false, true); } void window_caption(window wd, const std::string& title_utf8) { throw_not_utf8(title_utf8); - auto const iwd = reinterpret_cast(wd); internal_scope_guard lock; - if (restrict::wd_manager().available(iwd)) - iwd->widget_notifier->caption(to_nstring(title_utf8)); + if (is_window(wd)) + wd->widget_notifier->caption(to_nstring(title_utf8)); } void window_caption(window wd, const std::wstring& title) { - auto const iwd = reinterpret_cast(wd); internal_scope_guard lock; - if (restrict::wd_manager().available(iwd)) - iwd->widget_notifier->caption(to_nstring(title)); + if (is_window(wd)) + wd->widget_notifier->caption(to_nstring(title)); } std::string window_caption(window wd) { - auto const iwd = reinterpret_cast(wd); internal_scope_guard lock; - if (restrict::wd_manager().available(iwd)) - return to_utf8(iwd->widget_notifier->caption()); + if (is_window(wd)) + return to_utf8(wd->widget_notifier->caption()); return{}; } void window_cursor(window wd, cursor cur) { - auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - if(restrict::wd_manager().available(iwd)) + if(is_window(wd)) { - iwd->predef_cursor = cur; - restrict::bedrock.update_cursor(iwd); + wd->predef_cursor = cur; + restrict::bedrock.update_cursor(wd); } } cursor window_cursor(window wd) { - auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - if(restrict::wd_manager().available(iwd)) - return iwd->predef_cursor; + if(is_window(wd)) + return wd->predef_cursor; return cursor::arrow; } bool is_focus_ready(window wd) { - auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - if(restrict::wd_manager().available(iwd)) - return (iwd->root_widget->other.attribute.root->focus == iwd); + if(is_window(wd)) + return (wd->root_widget->other.attribute.root->focus == wd); return false; } void activate_window(window wd) { - auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - if (restrict::wd_manager().available(iwd)) + if (is_window(wd)) { - if(iwd->flags.take_active) - interface_type::activate_window(iwd->root); + if(wd->flags.take_active) + interface_type::activate_window(wd->root); } } window focus_window() { internal_scope_guard lock; - return reinterpret_cast(restrict::bedrock.focus()); + return restrict::bedrock.focus(); } void focus_window(window wd) { - restrict::wd_manager().set_focus(reinterpret_cast(wd), false, arg_focus::reason::general); - restrict::wd_manager().update(reinterpret_cast(wd), false, false); + restrict::wd_manager().set_focus(wd, false, arg_focus::reason::general); + restrict::wd_manager().update(wd, false, false); } window capture_window() { - return reinterpret_cast(restrict::wd_manager().capture_window()); + return restrict::wd_manager().capture_window(); } void set_capture(window wd, bool ignore_children) { - restrict::wd_manager().capture_window(reinterpret_cast(wd), true, ignore_children); + restrict::wd_manager().capture_window(wd, true, ignore_children); } void release_capture(window wd) { //The 3rd parameter is useless when the 2nd parameter is false. - restrict::wd_manager().capture_window(reinterpret_cast(wd), false, false); + restrict::wd_manager().capture_window(wd, false, false); } void modal_window(window wd) { - auto const iwd = reinterpret_cast(wd); - internal_scope_guard isg; + internal_scope_guard lock; - if (!restrict::wd_manager().available(iwd)) + if (empty_window(wd)) return; - if ((iwd->other.category == category::flags::root) && (iwd->flags.modal == false)) + if ((wd->other.category == category::flags::root) && (wd->flags.modal == false)) { - iwd->flags.modal = true; + wd->flags.modal = true; #if defined(NANA_X11) - interface_type::set_modal(iwd->root); + interface_type::set_modal(wd->root); #endif - restrict::wd_manager().show(iwd, true); + restrict::wd_manager().show(wd, true); } else return; @@ -1065,29 +1018,28 @@ namespace API void wait_for(window wd) { internal_scope_guard lock; - if (restrict::wd_manager().available(reinterpret_cast(wd))) + if (is_window(wd)) restrict::bedrock.pump_event(wd, false); } color fgcolor(window wd) { internal_scope_guard lock; - if (restrict::wd_manager().available(reinterpret_cast(wd))) - return reinterpret_cast(wd)->annex.scheme->foreground.get_color(); + if (is_window(wd)) + return wd->annex.scheme->foreground.get_color(); return{}; } color fgcolor(window wd, const color& clr) { - auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - if (restrict::wd_manager().available(iwd)) + if (is_window(wd)) { - auto prev = iwd->annex.scheme->foreground.get_color(); + auto prev = wd->annex.scheme->foreground.get_color(); if (prev != clr) { - iwd->annex.scheme->foreground = clr; - restrict::wd_manager().update(iwd, true, false); + wd->annex.scheme->foreground = clr; + refresh_window(wd); } return prev; } @@ -1097,27 +1049,26 @@ namespace API color bgcolor(window wd) { internal_scope_guard lock; - if (restrict::wd_manager().available(reinterpret_cast(wd))) - return reinterpret_cast(wd)->annex.scheme->background.get_color(); + if (is_window(wd)) + return wd->annex.scheme->background.get_color(); return{}; } color bgcolor(window wd, const color& clr) { - auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - if (restrict::wd_manager().available(iwd)) + if (is_window(wd)) { - auto prev = iwd->annex.scheme->background.get_color(); + auto prev = wd->annex.scheme->background.get_color(); if (prev != clr) { - iwd->annex.scheme->background = clr; + wd->annex.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; + if (wd->effect.bground && wd->effect.bground_fade_rate < 0.01) // fade rate < 0.01 means it is basic mode + wd->flags.make_bground_declared = true; - restrict::wd_manager().update(iwd, true, false); + refresh_window(wd); } return prev; } @@ -1127,22 +1078,21 @@ namespace API color activated_color(window wd) { internal_scope_guard lock; - if (restrict::wd_manager().available(reinterpret_cast(wd))) - return reinterpret_cast(wd)->annex.scheme->activated.get_color(); + if (is_window(wd)) + return wd->annex.scheme->activated.get_color(); return{}; } color activated_color(window wd, const color& clr) { - auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - if (restrict::wd_manager().available(iwd)) + if (is_window(wd)) { - auto prev = iwd->annex.scheme->activated.get_color(); + auto prev = wd->annex.scheme->activated.get_color(); if (prev != clr) { - iwd->annex.scheme->activated = clr; - restrict::wd_manager().update(iwd, true, false); + wd->annex.scheme->activated = clr; + refresh_window(wd); } return prev; } @@ -1231,7 +1181,7 @@ namespace API private: caret_interface* _m_caret() const { - if (restrict::wd_manager().available(window_) && window_->annex.caret_ptr) + if (is_window(window_) && window_->annex.caret_ptr) return window_->annex.caret_ptr; if (throw_) @@ -1246,27 +1196,25 @@ namespace API void create_caret(window wd, const size& caret_size) { - auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - if (restrict::wd_manager().available(iwd) && !(iwd->annex.caret_ptr)) - iwd->annex.caret_ptr = new ::nana::detail::caret(iwd, caret_size); + if (is_window(wd) && !(wd->annex.caret_ptr)) + wd->annex.caret_ptr = new ::nana::detail::caret(wd, caret_size); } void destroy_caret(window wd) { - auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - if(restrict::wd_manager().available(iwd)) + if(is_window(wd)) { - auto p = iwd->annex.caret_ptr; - iwd->annex.caret_ptr = nullptr; + auto p = wd->annex.caret_ptr; + wd->annex.caret_ptr = nullptr; delete p; } } std::unique_ptr open_caret(window window_handle, bool disable_throw) { - auto p = new caret_proxy{ reinterpret_cast(window_handle) }; + auto p = new caret_proxy{ window_handle }; if (disable_throw) p->disable_throw(); @@ -1275,72 +1223,63 @@ namespace API void tabstop(window wd) { - restrict::wd_manager().enable_tabstop(reinterpret_cast(wd)); + restrict::wd_manager().enable_tabstop(wd); } //eat_tabstop //@brief: set a eating tab window that it processes a pressing of tab itself void eat_tabstop(window wd, bool eat) { - if(wd) + internal_scope_guard lock; + if(is_window(wd)) { - auto iwd = reinterpret_cast(wd); - internal_scope_guard isg; - if(restrict::wd_manager().available(iwd)) - { - if(eat) - iwd->flags.tab |= ::nana::detail::tab_type::eating; - else - iwd->flags.tab &= ~::nana::detail::tab_type::eating; - } + if(eat) + wd->flags.tab |= ::nana::detail::tab_type::eating; + else + wd->flags.tab &= ~::nana::detail::tab_type::eating; } } window move_tabstop(window wd, bool next) { - basic_window* ts_wd = restrict::wd_manager().tabstop(reinterpret_cast(wd), next); - restrict::wd_manager().set_focus(ts_wd, false, arg_focus::reason::general); - restrict::wd_manager().update(ts_wd, false, false); - return reinterpret_cast(ts_wd); + auto prev_wd = restrict::wd_manager().tabstop(wd, next); + restrict::wd_manager().set_focus(prev_wd, false, arg_focus::reason::general); + restrict::wd_manager().update(prev_wd, false, false); + + return prev_wd; } void take_active(window wd, bool active, window take_if_active_false) { - auto const iwd = reinterpret_cast(wd); - auto take_if_false = reinterpret_cast(take_if_active_false); - internal_scope_guard lock; - if (restrict::wd_manager().available(iwd)) + if (is_window(wd)) { - if (active || (take_if_false && (restrict::wd_manager().available(take_if_false) == false))) - take_if_false = 0; + if (active || (take_if_active_false && empty_window(take_if_active_false))) + take_if_active_false = nullptr; - iwd->flags.take_active = active; - iwd->other.active_window = take_if_false; + wd->flags.take_active = active; + wd->other.active_window = take_if_active_false; } } bool window_graphics(window wd, nana::paint::graphics& graph) { - auto iwd = reinterpret_cast(wd); - internal_scope_guard lock; - if (!restrict::wd_manager().available(iwd)) + if (empty_window(wd)) return false; - graph.make(iwd->drawer.graphics.size()); - graph.bitblt(0, 0, iwd->drawer.graphics); - nana::detail::window_layout::paste_children_to_graphics(iwd, graph); + graph.make(wd->drawer.graphics.size()); + graph.bitblt(0, 0, wd->drawer.graphics); + nana::detail::window_layout::paste_children_to_graphics(wd, graph); return true; } bool root_graphics(window wd, nana::paint::graphics& graph) { - auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - if(restrict::wd_manager().available(iwd)) + if(is_window(wd)) { - graph = *(iwd->root_graph); + graph = *(wd->root_graph); return true; } return false; @@ -1348,51 +1287,47 @@ namespace API bool get_visual_rectangle(window wd, nana::rectangle& r) { - auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - if (restrict::wd_manager().available(iwd)) - return nana::detail::window_layout::read_visual_rectangle(iwd, r); + if (is_window(wd)) + return nana::detail::window_layout::read_visual_rectangle(wd, r); return false; } void typeface(window wd, const nana::paint::font& font) { - auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - if(restrict::wd_manager().available(iwd)) + if(is_window(wd)) { - iwd->drawer.graphics.typeface(font); - iwd->drawer.typeface_changed(); - restrict::wd_manager().update(iwd, true, false); + wd->drawer.graphics.typeface(font); + wd->drawer.typeface_changed(); + refresh_window(wd); } } nana::paint::font typeface(window wd) { - auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - if(restrict::wd_manager().available(iwd)) - return iwd->drawer.graphics.typeface(); + if(is_window(wd)) + return wd->drawer.graphics.typeface(); return{}; } bool calc_screen_point(window wd, nana::point& pos) { - auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - if(restrict::wd_manager().available(iwd)) + if(is_window(wd)) { - pos += iwd->pos_root; - return interface_type::calc_screen_point(iwd->root, pos); + pos += wd->pos_root; + return interface_type::calc_screen_point(wd->root, pos); } return false; } bool calc_window_point(window wd, nana::point& pos) { - return restrict::wd_manager().calc_window_point(reinterpret_cast(wd), pos); + return restrict::wd_manager().calc_window_point(wd, pos); } window find_window(const nana::point& pos) @@ -1402,65 +1337,59 @@ namespace API { ::nana::point clipos{pos}; interface_type::calc_window_point(wd, clipos); - return reinterpret_cast( - restrict::wd_manager().find_window(wd, clipos, true)); + return restrict::wd_manager().find_window(wd, clipos, true); } return nullptr; } bool is_window_zoomed(window wd, bool ask_for_max) { - auto const iwd = reinterpret_cast(wd); internal_scope_guard lock; - if (restrict::wd_manager().available(iwd)) + if (is_window(wd)) { - if (iwd->other.category == nana::category::flags::root) - return interface_type::is_window_zoomed(iwd->root, ask_for_max); + if (wd->other.category == nana::category::flags::root) + return interface_type::is_window_zoomed(wd->root, ask_for_max); } return false; } void widget_borderless(window wd, bool enabled) { - auto const iwd = reinterpret_cast(wd); internal_scope_guard lock; - if (restrict::wd_manager().available(iwd)) + if (is_window(wd)) { - if ((category::flags::widget == iwd->other.category) && (iwd->flags.borderless != enabled)) + if ((category::flags::widget == wd->other.category) && (wd->flags.borderless != enabled)) { - iwd->flags.borderless = enabled; - restrict::wd_manager().update(iwd, true, false); + wd->flags.borderless = enabled; + refresh_window(wd); } } } bool widget_borderless(window wd) { - auto const iwd = reinterpret_cast(wd); internal_scope_guard lock; - if (restrict::wd_manager().available(iwd)) - return iwd->flags.borderless; + if (is_window(wd)) + return wd->flags.borderless; return false; } nana::mouse_action mouse_action(window wd) { - auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - if(restrict::wd_manager().available(iwd)) - return iwd->flags.action; + if(is_window(wd)) + return wd->flags.action; return nana::mouse_action::normal; } nana::element_state element_state(window wd) { - auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - if(restrict::wd_manager().available(iwd)) + if(is_window(wd)) { - const bool is_focused = (iwd->root_widget->other.attribute.root->focus == iwd); - switch(iwd->flags.action) + const bool is_focused = (wd->root_widget->other.attribute.root->focus == wd); + switch(wd->flags.action) { case nana::mouse_action::normal: case nana::mouse_action::normal_captured: @@ -1470,7 +1399,7 @@ namespace API case nana::mouse_action::pressed: return nana::element_state::pressed; default: - if(false == iwd->flags.enabled) + if(false == wd->flags.enabled) return nana::element_state::disabled; } } @@ -1479,12 +1408,11 @@ namespace API bool ignore_mouse_focus(window wd, bool ignore) { - auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - if (restrict::wd_manager().available(iwd)) + if (is_window(wd)) { - auto state = iwd->flags.ignore_mouse_focus; - iwd->flags.ignore_mouse_focus = ignore; + auto state = wd->flags.ignore_mouse_focus; + wd->flags.ignore_mouse_focus = ignore; return state; } return false; @@ -1492,24 +1420,22 @@ namespace API bool ignore_mouse_focus(window wd) { - auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - return (restrict::wd_manager().available(iwd) ? iwd->flags.ignore_mouse_focus : false); + return (is_window(wd) ? wd->flags.ignore_mouse_focus : false); } void at_safe_place(window wd, std::function fn) { - restrict::wd_manager().set_safe_place(reinterpret_cast(wd), std::move(fn)); + restrict::wd_manager().set_safe_place(wd, std::move(fn)); } std::optional> content_extent(window wd, unsigned limited_px, bool limit_width) { - auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - if (restrict::wd_manager().available(iwd) && iwd->annex.content_measurer) + if (is_window(wd) && wd->annex.content_measurer) { - paint::graphics* graph = &iwd->drawer.graphics; + paint::graphics* graph = &wd->drawer.graphics; paint::graphics temp_graph; if (graph->empty()) { @@ -1518,9 +1444,9 @@ namespace API graph = &temp_graph; } - auto extent = iwd->annex.content_measurer->measure(*graph, limited_px, limit_width); + auto extent = wd->annex.content_measurer->measure(*graph, limited_px, limit_width); if (extent) - return std::make_pair(extent.value(), extent.value() + iwd->annex.content_measurer->extension()); + return std::make_pair(extent.value(), extent.value() + wd->annex.content_measurer->extension()); } return{}; @@ -1533,11 +1459,9 @@ namespace API dragdrop_status window_dragdrop_status(::nana::window wd) { - auto real_wd = reinterpret_cast(wd); internal_scope_guard lock; - - if (restrict::wd_manager().available(real_wd)) - return real_wd->other.dnd_state; + if (is_window(wd)) + return wd->other.dnd_state; return dragdrop_status::not_ready; } diff --git a/source/gui/state_cursor.cpp b/source/gui/state_cursor.cpp index 5644ed83..817b6dec 100644 --- a/source/gui/state_cursor.cpp +++ b/source/gui/state_cursor.cpp @@ -9,18 +9,18 @@ * * @file: nana/gui/state_cursor.cpp */ + +#include "detail/basic_window.hpp" #include #include -#include #include namespace nana { - state_cursor::state_cursor(window handle, cursor cur) - : handle_(handle) + state_cursor::state_cursor(window wd, cursor cur) + : handle_(wd) { auto & brock = detail::bedrock::instance(); - auto wd = reinterpret_cast(handle); if (brock.wd_manager().available(wd)) brock.define_state_cursor(wd, cur, nullptr); else @@ -39,10 +39,9 @@ namespace nana { if (handle_) { + nana::internal_scope_guard lock; auto & brock = detail::bedrock::instance(); - auto wd = reinterpret_cast(handle_); - if (brock.wd_manager().available(wd)) - brock.undefine_state_cursor(wd, nullptr); + brock.undefine_state_cursor(handle_, nullptr); } handle_ = rhs.handle_; rhs.handle_ = nullptr; @@ -54,10 +53,9 @@ namespace nana { if (handle_) { + nana::internal_scope_guard lock; auto & brock = detail::bedrock::instance(); - auto wd = reinterpret_cast(handle_); - if (brock.wd_manager().available(wd)) - brock.undefine_state_cursor(wd, nullptr); + brock.undefine_state_cursor(handle_, nullptr); } } } \ No newline at end of file diff --git a/source/gui/timer.cpp b/source/gui/timer.cpp index 52981a8e..8374a5fb 100644 --- a/source/gui/timer.cpp +++ b/source/gui/timer.cpp @@ -28,24 +28,26 @@ #if defined(NANA_WINDOWS) #include #elif defined(NANA_POSIX) -#include "../detail/platform_spec_selector.hpp" +#include "../detail/posix/platform_spec.hpp" #include #endif namespace nana { - class timer_core; + namespace detail + { + class timer_core; + } + #if defined(NANA_WINDOWS) typedef UINT_PTR timer_identifier; #else - typedef timer_core* timer_identifier; + typedef const detail::timer_core* timer_identifier; #endif class timer_driver { - typedef std::lock_guard lock_guard; - - friend class timer_core; + friend class detail::timer_core; timer_driver() = default; public: @@ -56,7 +58,7 @@ namespace nana } template - timer_core* create(unsigned ms, Factory && factory) + detail::timer_core* create(unsigned ms, Factory && factory) { #if defined(NANA_WINDOWS) auto tmid = ::SetTimer(nullptr, 0, ms, &timer_driver::_m_timer_proc); @@ -68,9 +70,9 @@ namespace nana #else auto p = factory(); auto tmid = p; - ::nana::detail::platform_spec::instance().set_timer(reinterpret_cast(tmid), ms, &timer_driver::_m_timer_proc); + ::nana::detail::platform_spec::instance().set_timer(tmid, ms, &timer_driver::_m_timer_proc); #endif - lock_guard lock(mutex_); + ::nana::internal_scope_guard lock; timer_table_[tmid].reset(p); return p; } @@ -80,16 +82,17 @@ namespace nana return nullptr; } - void destroy(timer_identifier tid) + void destroy(timer_identifier handle) { - lock_guard lock(mutex_); - auto i = timer_table_.find(tid); + ::nana::internal_scope_guard lock; + + auto i = timer_table_.find(handle); if (i != timer_table_.end()) { #if defined(NANA_WINDOWS) - ::KillTimer(nullptr, tid); + ::KillTimer(nullptr, handle); #else - ::nana::detail::platform_spec::instance().kill_timer(reinterpret_cast(tid)); + ::nana::detail::platform_spec::instance().kill_timer(handle); #endif timer_table_.erase(i); } @@ -98,23 +101,26 @@ namespace nana #if defined(NANA_WINDOWS) static void __stdcall _m_timer_proc(HWND hwnd, UINT uMsg, UINT_PTR id, DWORD dwTime); #else - static void _m_timer_proc(std::size_t id); + static void _m_timer_proc(timer_identifier id); #endif private: - std::recursive_mutex mutex_; - std::map> timer_table_; + std::map> timer_table_; }; - class timer_core + class detail::timer_core { public: #if defined(NANA_WINDOWS) - timer_core(timer_identifier tmid, basic_event& evt_elapse) - : timer_(tmid), evt_elapse_(evt_elapse) + timer_core(timer* sender, timer_identifier tmid, basic_event& evt_elapse): + sender_(sender), + timer_(tmid), + evt_elapse_(evt_elapse) {} #else - timer_core(basic_event& evt_elapse) - : timer_(this), evt_elapse_(evt_elapse) + timer_core(timer* sender, basic_event& evt_elapse): + sender_(sender), + timer_(this), + evt_elapse_(evt_elapse) {} #endif @@ -128,45 +134,44 @@ namespace nana #if defined(NANA_WINDOWS) ::SetTimer(nullptr, timer_, ms, &timer_driver::_m_timer_proc); #else - ::nana::detail::platform_spec::instance().set_timer(reinterpret_cast(timer_), ms, &timer_driver::_m_timer_proc); + ::nana::detail::platform_spec::instance().set_timer(timer_, ms, &timer_driver::_m_timer_proc); #endif } - void emit(const arg_elapse& arg) + void emit() { + arg_elapse arg; + arg.sender = sender_; evt_elapse_.emit(arg, nullptr); } private: + timer * const sender_; const timer_identifier timer_; nana::basic_event & evt_elapse_; }; //end class timer_core + #if defined(NANA_WINDOWS) - void __stdcall timer_driver::_m_timer_proc(HWND /*hwnd*/, UINT /*uMsg*/, UINT_PTR id, DWORD /*dwTime*/) + void __stdcall timer_driver::_m_timer_proc(HWND /*hwnd*/, UINT /*uMsg*/, UINT_PTR handle, DWORD /*dwTime*/) #else - void timer_driver::_m_timer_proc(std::size_t id) + void timer_driver::_m_timer_proc(timer_identifier handle) #endif { - auto & driver = instance(); + auto & time_tbl = instance().timer_table_; - lock_guard lock(driver.mutex_); -#if defined(NANA_WINDOWS) - auto i = driver.timer_table_.find(id); -#else - auto i = driver.timer_table_.find(reinterpret_cast(id)); -#endif - if (i == driver.timer_table_.end()) + ::nana::internal_scope_guard lock; + + auto i = time_tbl.find(handle); + if (i == time_tbl.end()) return; - arg_elapse arg; - arg.id = id; - i->second->emit(arg); + i->second->emit(); } struct timer::implement { - unsigned interval = 1000; //Defaultly 1 second. - timer_core * tm_core = nullptr; + unsigned interval{ 1000 }; //1 second in default + detail::timer_core * tm_core{ nullptr }; }; //class timer @@ -183,8 +188,7 @@ namespace nana timer::~timer() { - if (impl_->tm_core) - timer_driver::instance().destroy(impl_->tm_core->id()); + stop(); delete impl_; } @@ -201,12 +205,12 @@ namespace nana #if defined(NANA_WINDOWS) impl_->tm_core = timer_driver::instance().create(impl_->interval, [this](timer_identifier id) { - return new timer_core(id, elapse_); + return new detail::timer_core(this, id, elapse_); }); #else impl_->tm_core = timer_driver::instance().create(impl_->interval, [this] { - return new timer_core(elapse_); + return new detail::timer_core(this, elapse_); }); #endif } diff --git a/source/gui/widgets/button.cpp b/source/gui/widgets/button.cpp index 96ff7c25..ecc09817 100644 --- a/source/gui/widgets/button.cpp +++ b/source/gui/widgets/button.cpp @@ -261,7 +261,7 @@ namespace nana{ namespace drawerbase graph.palette(true, text_color); if (attr_.omitted) - tr.render(pos, txtptr, txtlen, omitted_pixels, true); + tr.render(pos, txtptr, txtlen, omitted_pixels, paint::text_renderer::mode::truncate_with_ellipsis); else #ifdef _nana_std_has_string_view graph.bidi_string(pos, { txtptr, txtlen }); @@ -276,9 +276,9 @@ namespace nana{ namespace drawerbase graph.palette(true, color{ colors::white }); if(attr_.omitted) { - tr.render(point{ pos.x + 1, pos.y + 1 }, txtptr, txtlen, omitted_pixels, true); + tr.render(point{ pos.x + 1, pos.y + 1 }, txtptr, txtlen, omitted_pixels, paint::text_renderer::mode::truncate_with_ellipsis); graph.palette(true, color{ colors::gray }); - tr.render(pos, txtptr, txtlen, omitted_pixels, true); + tr.render(pos, txtptr, txtlen, omitted_pixels, paint::text_renderer::mode::truncate_with_ellipsis); } else { diff --git a/source/gui/widgets/categorize.cpp b/source/gui/widgets/categorize.cpp index 19d3ab23..596d7cd3 100644 --- a/source/gui/widgets/categorize.cpp +++ b/source/gui/widgets/categorize.cpp @@ -1,16 +1,16 @@ -/* +/** * A Categorize Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 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/categorize.cpp + * @file nana/gui/widgets/categorize.cpp */ -#include +#include #include #include #include diff --git a/source/gui/widgets/checkbox.cpp b/source/gui/widgets/checkbox.cpp index fc08fcac..4741d5b2 100644 --- a/source/gui/widgets/checkbox.cpp +++ b/source/gui/widgets/checkbox.cpp @@ -70,13 +70,13 @@ namespace nana{ namespace drawerbase if (!wdg->enabled()) { graph.palette(true, colors::white); - tr.render({ 17 + interval, 2 }, title.c_str(), title.length(), pixels); + tr.render({ 17 + interval, 2 }, title.c_str(), title.length(), pixels, paint::text_renderer::mode::word_wrap); graph.palette(true, static_cast(0x808080)); } else graph.palette(true, wdg->fgcolor()); - tr.render({ 16 + interval, 1 }, title.c_str(), title.length(), pixels); + tr.render({ 16 + interval, 1 }, title.c_str(), title.length(), pixels, paint::text_renderer::mode::word_wrap); } //draw crook diff --git a/source/gui/widgets/combox.cpp b/source/gui/widgets/combox.cpp index c5ef5f85..816d4ee9 100644 --- a/source/gui/widgets/combox.cpp +++ b/source/gui/widgets/combox.cpp @@ -10,9 +10,9 @@ * @file: nana/gui/widgets/combox.cpp */ -#include -#include +#include #include +#include #include #include #include diff --git a/source/gui/widgets/group.cpp b/source/gui/widgets/group.cpp index 726814e8..b22b662b 100644 --- a/source/gui/widgets/group.cpp +++ b/source/gui/widgets/group.cpp @@ -1,19 +1,19 @@ /** * A group widget implementation * Nana C++ Library(http://www.nanaro.org) - * Copyright(C) 2015-2018 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2015-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at + * (See accompanying file LICENSE or copy at * http://www.boost.org/LICENSE_1_0.txt) * - * @file: nana/gui/widgets/group.cpp + * @file nana/gui/widgets/group.cpp * - * @Author: Stefan Pfeifer(st-321), Ariel Vina-Rodriguez (qPCR4vir) + * @author Stefan Pfeifer(st-321), Ariel Vina-Rodriguez (qPCR4vir) * * @brief group is a widget used to visually group and layout other widgets. * - * @contributor: + * @contributor * dankan1890(https://github.com/dankan1890) */ diff --git a/source/gui/widgets/listbox.cpp b/source/gui/widgets/listbox.cpp index 00e2052a..460e28f4 100644 --- a/source/gui/widgets/listbox.cpp +++ b/source/gui/widgets/listbox.cpp @@ -15,7 +15,7 @@ * Benjamin Navarro(pr#81) * besh81(pr#130) * dankan1890(pr#158) - * ErrorFlynn(pr#418) + * ErrorFlynn(pr#418,pr#448,pr#454) * */ #include @@ -549,9 +549,9 @@ namespace nana if (!front) view++; if (view >= cont_.size() ) return; - auto i = std::find_if( cont_.begin(), - cont_.end(), - [&](const column& c){return col==c.index;}); + auto i = std::find_if( cont_.begin(), + cont_.end(), + [&](const column& c){return col==c.index;}); if (i==cont_.end()) return; @@ -1199,23 +1199,15 @@ namespace nana return{}; } - /// 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. + /// return a ref to the real item object at display position category_t::container::value_type& at(const index_pair& pos) { - auto acc_pos = pos.item; - if (npos != sort_attrs_.column) - acc_pos = index_cast(pos, true).item; //convert display position to absolute position - - return get(pos.cat)->items.at(acc_pos); + return get(pos.cat)->items.at(index_cast(pos, true).item); } const category_t::container::value_type& at(const index_pair& pos) const { - auto acc_pos = pos.item; - if (npos != sort_attrs_.column) - acc_pos = index_cast(pos, true).item; //convert display position to absolute position - - return get(pos.cat)->items.at(acc_pos); + return get(pos.cat)->items.at(index_cast(pos, true).item); } std::vector at_model(const index_pair& pos) const @@ -2019,6 +2011,8 @@ namespace nana std::function ctg_icon_renderer; ///< Renderer for the category icon + std::function pred_msup_deselect; + struct operation_rep { operation_states state{operation_states::none}; @@ -2065,6 +2059,11 @@ namespace nana }; } + unsigned suspension_width() const + { + return (graph ? graph->text_extent_size(L"...").width : 0); + } + bool cs_status(index_pair abs_pos, bool for_selection) const { if (abs_pos.is_category()) @@ -3358,7 +3357,7 @@ namespace nana else { //Default scheme - new_w = (std::max)(new_w, essence_->scheme_ptr->suspension_width + essence_->scheme_ptr->min_column_width); + new_w = (std::max)(new_w, essence_->suspension_width() + essence_->scheme_ptr->min_column_width); } if(col.width_px != new_w) @@ -4003,6 +4002,7 @@ namespace nana if (draw_column) { + //Draw item text paint::aligner text_aligner{*graph, col.alignment}; unsigned text_margin_right = 0; @@ -4148,7 +4148,6 @@ namespace nana if (graph.text_metrics(as, ds, il)) essence_->text_height = as + ds; - essence_->scheme_ptr->suspension_width = graph.text_extent_size("...").width; essence_->calc_content_size(true); } @@ -4343,7 +4342,7 @@ namespace nana else { auto selected = lister.pick_items(true); - if (selected.cend() != std::find(selected.cbegin(), selected.cend(), item_pos)) + if (selected.cend() != std::find(selected.cbegin(), selected.cend(), abs_item_pos)) { //If the current selected one has been selected before selecting, remains the selection states for all //selected items. But these items will be unselected when the mouse is released. @@ -4359,6 +4358,8 @@ namespace nana } else lister.select_for_all(false, abs_item_pos); + + lister.latest_selected_abs = abs_item_pos; } } else @@ -4474,11 +4475,14 @@ namespace nana essence_->stop_mouse_selection(); need_refresh = true; } - + if (operation_states::msup_deselect == essence_->operation.state) { essence_->operation.state = operation_states::none; - need_refresh |= essence_->lister.select_for_all(false, essence_->operation.item); + + //Don't deselect if the predicate returns false + if(!(essence_->pred_msup_deselect && !essence_->pred_msup_deselect(arg.button))) + need_refresh |= essence_->lister.select_for_all(false, essence_->operation.item); } if (need_refresh) @@ -6043,7 +6047,7 @@ namespace nana return *this; } - listbox& listbox::category_icon(const paint::image& img_expanded, const paint::image&& img_collapsed) + listbox& listbox::category_icon(const paint::image& img_expanded, const paint::image& img_collapsed) { internal_scope_guard lock; _m_ess().ctg_icon_renderer = [img_expanded, img_collapsed](paint::graphics& graph, const rectangle& rt_icon, bool expanded) @@ -6110,6 +6114,17 @@ namespace nana return indexes; } + void listbox::set_deselect(std::function predicate) + { + _m_ess().pred_msup_deselect = std::move(predicate); + } + + unsigned listbox::suspension_width() const + { + nana::internal_scope_guard lock; + return _m_ess().suspension_width(); + } + drawerbase::listbox::essence & listbox::_m_ess() const { return get_drawer_trigger().ess(); @@ -6193,7 +6208,7 @@ namespace nana std::vector new_idx; for(size_type i=first_col; i<=last_col; ++i) new_idx.push_back(i); - + internal_scope_guard lock; auto ip_row = this->at(row); auto pnany=_m_ess().lister.anyobj(row,false); diff --git a/source/gui/widgets/menu.cpp b/source/gui/widgets/menu.cpp index e3b181e4..078844aa 100644 --- a/source/gui/widgets/menu.cpp +++ b/source/gui/widgets/menu.cpp @@ -13,12 +13,13 @@ * dankan1890(pr#158) */ +#include +#include #include #include #include #include -#include #include #include //introduces tolower #include @@ -147,7 +148,7 @@ namespace nana nana::paint::text_renderer tr(graph); auto wstr = to_wstring(text); - tr.render(pos, wstr.c_str(), wstr.length(), text_pixels, true); + tr.render(pos, wstr.c_str(), wstr.length(), text_pixels, paint::text_renderer::mode::truncate_with_ellipsis); } void sub_arrow(graph_reference graph, const nana::point& pos, unsigned pixels, const attr&) diff --git a/source/gui/widgets/tabbar.cpp b/source/gui/widgets/tabbar.cpp index 4cf17919..6a79c648 100644 --- a/source/gui/widgets/tabbar.cpp +++ b/source/gui/widgets/tabbar.cpp @@ -1050,7 +1050,7 @@ namespace nana std::wstring wtext = to_wstring(item.text); tr.render({ m.r.x + 24, m.r.y + static_cast(m.r.height - ts.height) / 2 }, - wtext.c_str(), wtext.length(), basis_.item_pixels - 24 - 18, true); + wtext.c_str(), wtext.length(), basis_.item_pixels - 24 - 18, paint::text_renderer::mode::truncate_with_ellipsis); } } diff --git a/source/gui/widgets/toolbar.cpp b/source/gui/widgets/toolbar.cpp index 963b99cf..f8db43c4 100644 --- a/source/gui/widgets/toolbar.cpp +++ b/source/gui/widgets/toolbar.cpp @@ -27,26 +27,6 @@ namespace nana { namespace toolbar { - struct item_type - { - enum kind{ button, container}; - - typedef std::size_t size_type; - - std::string text; - nana::paint::image image; - unsigned pixels{0}; - unsigned position{ 0 }; // last item position. - nana::size textsize; - bool enable{true}; - - kind type; - - item_type(const std::string& text, const nana::paint::image& img, kind type) - :text(text), image(img), type(type) - {} - }; - class item_container { public: @@ -58,7 +38,7 @@ namespace nana clear(); } - void insert(size_type pos, std::string text, const nana::paint::image& img, item_type::kind type) + void insert(size_type pos, std::string text, const nana::paint::image& img, tool_type type) { item_type* m = new item_type(std::move(text), img, type); @@ -70,12 +50,12 @@ namespace nana void push_back(const std::string& text, const nana::paint::image& img) { - insert(cont_.size(), text, img, item_type::kind::button); + insert(cont_.size(), text, img, tool_type::button); } void push_back(const std::string& text) { - insert(cont_.size(), text, nana::paint::image(), item_type::kind::button); + insert(cont_.size(), text, nana::paint::image(), tool_type::button); } //Contributed by kmribti(pr#105) @@ -126,6 +106,45 @@ namespace nana cont_.clear(); } + + void update_toggle_group(item_type* item, bool toggle_state, bool clicked = true) + { + if(!item) + return; + + if(item->group.empty()) + { + item->toggle = toggle_state; + return; + } + + // group rules: + // 1. inside a group only one item at the time is selected + // 2. inside a group one item must always be selected + // 3. a group with only one item IS NOT a group + + bool is_group = false; + + // look for other items inside the group + for(auto i : cont_) + { + if(i == item) + continue; + + if(i && i->group == item->group) + { + if(toggle_state == false && clicked == false) // needs to avoid to break rule no. 2 + return; + + is_group = true; + i->toggle = false; + } + } + + item->toggle = is_group ? true : toggle_state; + } + + private: container_type cont_; size_t right_{ npos }; @@ -137,8 +156,8 @@ namespace nana enum class state_t{normal, highlighted, selected}; const static unsigned extra_size = 6; - item_renderer(nana::paint::graphics& graph, bool textout, unsigned scale, const ::nana::color& bgcolor) - :graph(graph), textout(textout), scale(scale), bgcolor(bgcolor) + item_renderer(nana::paint::graphics& graph, unsigned scale, const ::nana::color& bgcolor, const ::nana::color& fgcolor) + :graph(graph), scale(scale), bgcolor(bgcolor), fgcolor(fgcolor) {} void operator()(int x, int y, unsigned width, unsigned height, item_type& item, state_t state) @@ -152,6 +171,13 @@ namespace nana 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); } + else if (item.type == tool_type::toggle && item.toggle) + { + nana::rectangle background_r(x, y, width, height); + graph.rectangle(background_r, false, static_cast(item.enable ? 0x3399FF : 0x999999)); + + graph.gradual_rectangle(background_r.pare_off(1), bgcolor, static_cast(item.enable ? 0xC0DDFC : 0x969696), true); + } if(!item.image.empty()) { @@ -181,17 +207,17 @@ namespace nana width -= scale; } - if(textout) + if(item.textout) { - graph.string({ x + static_cast(width - item.textsize.width) / 2, y + static_cast(height - item.textsize.height) / 2 }, item.text); + graph.string({ x + static_cast(width - item.textsize.width) / 2, y + static_cast(height - item.textsize.height) / 2 }, item.text, fgcolor ); } } protected: nana::paint::graphics& graph; - bool textout; unsigned scale; ::nana::color bgcolor; + ::nana::color fgcolor; }; struct drawer::drawer_impl_type @@ -200,7 +226,6 @@ namespace nana 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}; @@ -237,10 +262,11 @@ namespace nana int x = 2, y = 2; auto bgcolor = API::bgcolor(widget_->handle()); + auto fgcolor = API::fgcolor(widget_->handle()); graph.palette(true, bgcolor); graph.gradual_rectangle(rectangle{ graph.size() }, bgcolor.blend(colors::white, 0.1), bgcolor.blend(colors::black, 0.05), true); - item_renderer ir(graph, impl_->textout, impl_->scale, bgcolor); + item_renderer ir(graph, impl_->scale, bgcolor, fgcolor); size_type index = 0; for (auto item : impl_->items.container()) @@ -378,6 +404,10 @@ namespace nana size_type which = _m_which(arg.pos, false); if(impl_->which == which) { + // update toggle state + auto m = impl_->items.at(impl_->which); + impl_->items.update_toggle_group(m, !m->toggle); + ::nana::arg_toolbar arg{ *widget_, which }; widget_->events().selected.emit(arg, widget_->handle()); @@ -419,19 +449,76 @@ namespace nana if (item->text.size()) item->textsize = impl_->graph_ptr->text_extent_size(item->text); - if (item->image.empty() == false) + if(item->image.empty()) + { + if(item->textsize.width && item->textout) + item->pixels = item->textsize.width + 8; + else + item->pixels = impl_->scale + item_renderer::extra_size; + } + else + { item->pixels = impl_->scale + item_renderer::extra_size; - - if (item->textsize.width && impl_->textout) - item->pixels += item->textsize.width + 8; + if(item->textsize.width && item->textout) + item->pixels += item->textsize.width + 8; + } } } //class drawer // Item Proxy - void item_proxy::enable(bool enable_state) + item_proxy::item_proxy(::nana::toolbar* t, std::size_t pos) + : tb_{ t }, pos_{ pos } + {} + + bool item_proxy::enable() const { - widget.enable(button, enable_state); + return tb_->enable(pos_); + } + + item_proxy& item_proxy::enable(bool enable_state) + { + tb_->enable(pos_, enable_state); + return *this; + } + + item_proxy& item_proxy::tooltype(tool_type type) + { + tb_->tooltype(pos_, type); + return *this; + } + + bool item_proxy::istoggle() const + { + return tb_->istoggle(pos_); + } + + bool item_proxy::toggle() const + { + return tb_->toggle(pos_); + } + + item_proxy& item_proxy::toggle(bool toggle_state) + { + tb_->toggle(pos_, toggle_state); + return *this; + } + + std::string item_proxy::toggle_group() const + { + return tb_->toggle_group(pos_); + } + + item_proxy& item_proxy::textout(bool show) + { + tb_->textout(pos_, show); + return *this; + } + + item_proxy& item_proxy::toggle_group(const ::std::string& group) + { + tb_->toggle_group(pos_, group); + return *this; } }//end namespace toolbar }//end namespace drawerbase @@ -465,14 +552,14 @@ namespace nana { get_drawer_trigger().items().push_back(text, img); API::refresh_window(handle()); - return {*this, get_drawer_trigger().items().size() - 1u}; + return {this, get_drawer_trigger().items().size() - 1u}; } drawerbase::toolbar::item_proxy toolbar::append(const std::string& text) { get_drawer_trigger().items().push_back(text, {}); API::refresh_window(this->handle()); - return {*this, get_drawer_trigger().items().size() - 1u}; + return {this, get_drawer_trigger().items().size() - 1u}; } void toolbar::clear() @@ -507,6 +594,100 @@ namespace nana } } + void toolbar::tooltype(size_type index, tool_type type) + { + auto & items = get_drawer_trigger().items(); + + if(items.size() > index) + { + auto m = items.at(index); + if(m && m->type != type) + { + m->type = type; + API::refresh_window(this->handle()); + } + } + } + + bool toolbar::istoggle(size_type index) const + { + auto & items = get_drawer_trigger().items(); + + if(items.size() <= index) + return false; + + auto m = items.at(index); + return (m && m->type == tool_type::toggle); + } + + bool toolbar::toggle(size_type index) const + { + auto & items = get_drawer_trigger().items(); + + if(items.size() <= index) + return false; + + auto m = items.at(index); + return (m && m->toggle); + } + + void toolbar::toggle(size_type index, bool toggle_state) + { + auto & items = get_drawer_trigger().items(); + + if(items.size() > index) + { + auto m = items.at(index); + if(m) + { + items.update_toggle_group(m, toggle_state, false); + + API::refresh_window(this->handle()); + } + } + } + + std::string toolbar::toggle_group(size_type index) const + { + auto & items = get_drawer_trigger().items(); + + if(items.size() <= index) + return ""; + + auto m = items.at(index); + return m ? m->group : ""; + } + + void toolbar::toggle_group(size_type index, const ::std::string& group) + { + auto & items = get_drawer_trigger().items(); + + if(items.size() > index) + { + auto m = items.at(index); + if(m && (m->group != group)) + { + m->group = group; + API::refresh_window(this->handle()); + } + } + } + + void toolbar::textout(size_type index, bool show) + { + auto & items = get_drawer_trigger().items(); + + if(items.size() > index) + { + auto m = items.at(index); + if(m && (m->textout != show)) + { + m->textout = show; + API::refresh_window(this->handle()); + } + } + } + void toolbar::scale(unsigned s) { get_drawer_trigger().scale(s); diff --git a/source/gui/wvl.cpp b/source/gui/wvl.cpp index 7497d3c4..103355b1 100644 --- a/source/gui/wvl.cpp +++ b/source/gui/wvl.cpp @@ -1,6 +1,6 @@ /* * Nana GUI Library Definition - * Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -11,7 +11,8 @@ * the file contains the files required for running of Nana.GUI */ -#include +#include +#include #include #include #include @@ -29,7 +30,7 @@ namespace nana { void form_loader_private::insert_form(::nana::widget* p) { - bedrock::instance().manage_form_loader(reinterpret_cast(p->handle()), true); + bedrock::instance().manage_form_loader(p->handle(), true); } } diff --git a/source/paint/detail/image_png.hpp b/source/paint/detail/image_png.hpp index 91dd8e2b..8125fe99 100644 --- a/source/paint/detail/image_png.hpp +++ b/source/paint/detail/image_png.hpp @@ -30,16 +30,21 @@ namespace nana png_byte color_type = ::png_get_color_type(png_ptr, info_ptr); const auto bit_depth = ::png_get_bit_depth(png_ptr, info_ptr); + pixbuf_.open(png_width, png_height); + //do some extra work for palette/gray color type if (PNG_COLOR_TYPE_PALETTE == color_type) ::png_set_palette_to_rgb(png_ptr); - else if ((PNG_COLOR_TYPE_GRAY == color_type) && (bit_depth < 8)) + else if ((PNG_COLOR_TYPE_GRAY == color_type) || (PNG_COLOR_TYPE_GRAY_ALPHA == color_type)) ::png_set_gray_to_rgb(png_ptr); auto is_alpha_enabled = (::png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) != 0); if (is_alpha_enabled) ::png_set_tRNS_to_alpha(png_ptr); + is_alpha_enabled |= ((PNG_COLOR_MASK_ALPHA & color_type) != 0); + pixbuf_.alpha_channel(is_alpha_enabled); + //make sure 8-bit per channel if (16 == bit_depth) ::png_set_strip_16(png_ptr); @@ -51,11 +56,6 @@ namespace nana png_bytep * row_ptrs = new png_bytep[png_height]; const std::size_t png_rowbytes = ::png_get_rowbytes(png_ptr, info_ptr); - pixbuf_.open(png_width, png_height); - - is_alpha_enabled |= ((PNG_COLOR_MASK_ALPHA & color_type) != 0); - pixbuf_.alpha_channel(is_alpha_enabled); - if (is_alpha_enabled && (png_rowbytes == png_width * sizeof(pixel_argb_t))) { for (int i = 0; i < png_height; ++i) @@ -63,14 +63,17 @@ namespace nana ::png_read_image(png_ptr, row_ptrs); - for (int i = 0; i < png_height; ++i) + if (std::is_same::value) { - auto p = pixbuf_.raw_ptr(i); - for (int u = 0; u < png_width; ++u) + for (int i = 0; i < png_height; ++i) { - auto t = p[u].element.red; - p[u].element.red = p[u].element.blue; - p[u].element.blue = t; + auto p = pixbuf_.raw_ptr(i); + for (int u = 0; u < png_width; ++u) + { + auto t = p[u].element.red; + p[u].element.red = p[u].element.blue; + p[u].element.blue = t; + } } } } @@ -82,7 +85,6 @@ namespace nana row_ptrs[i] = reinterpret_cast(png_pixbuf + png_rowbytes * i); ::png_read_image(png_ptr, row_ptrs); - //::png_destroy_read_struct(&png_ptr, &info_ptr, 0); std::size_t png_pixel_bytes = png_rowbytes / png_width; diff --git a/source/paint/detail/native_paint_interface.cpp b/source/paint/detail/native_paint_interface.cpp index 91b34a81..3d41b9a7 100644 --- a/source/paint/detail/native_paint_interface.cpp +++ b/source/paint/detail/native_paint_interface.cpp @@ -1,7 +1,7 @@ /* * Platform Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -24,6 +24,14 @@ namespace nana { +#ifdef NANA_USE_XFT + //Forward-declarations + //These names are defined platform_abstraction.cpp + class font_interface; + void nana_xft_draw_string(::XftDraw* xftdraw, ::XftColor* xftcolor, font_interface* ft, const nana::point& pos, const wchar_t * str, std::size_t len); + nana::size nana_xft_extents(font_interface* ft, const wchar_t* str, std::size_t len); +#endif + namespace paint { namespace detail @@ -144,14 +152,19 @@ namespace detail if (::GetTextExtentPoint32(dw->context, text, static_cast(len), &size)) return nana::size(size.cx, size.cy); #elif defined(NANA_X11) - std::string utf8text = to_utf8(std::wstring(text, len)); #if defined(NANA_USE_XFT) + #if 0 + std::string utf8text = to_utf8(std::wstring(text, len)); XGlyphInfo ext; XftFont * fs = reinterpret_cast(dw->font->native_handle()); ::XftTextExtentsUtf8(nana::detail::platform_spec::instance().open_display(), fs, reinterpret_cast(const_cast(utf8text.data())), utf8text.size(), &ext); return nana::size(ext.xOff, fs->ascent + fs->descent); + #else + return nana_xft_extents(dw->font.get(), text, len); + #endif #else + std::string utf8text = to_utf8(std::wstring(text, len)); XRectangle ink; XRectangle logic; ::XmbTextExtents(reinterpret_cast(dw->font->native_handle()), utf8text.c_str(), utf8text.size(), &ink, &logic); @@ -178,11 +191,20 @@ namespace detail return nana::size(size.cx, size.cy); #elif defined(NANA_X11) #if defined(NANA_USE_XFT) + #if 0 XGlyphInfo ext; XftFont * fs = reinterpret_cast(dw->font->native_handle()); ::XftTextExtentsUtf8(nana::detail::platform_spec::instance().open_display(), fs, reinterpret_cast(const_cast(text)), len, &ext); return nana::size(ext.xOff, fs->ascent + fs->descent); + #else +#ifdef _nana_std_has_string_view + auto wstr = to_wstring(std::string_view(text, len)); +#else + auto wstr = to_wstring(std::string(text,len)); +#endif + return nana_xft_extents(dw->font.get(), wstr.data(), wstr.size()); + #endif #else XRectangle ink; XRectangle logic; @@ -238,19 +260,8 @@ namespace detail #if defined(NANA_WINDOWS) ::TextOut(dw->context, pos.x, pos.y, str, static_cast(len)); #elif defined(NANA_X11) - auto disp = ::nana::detail::platform_spec::instance().open_display(); #if defined(NANA_USE_XFT) - auto fs = reinterpret_cast(dw->font->native_handle()); - - //Fixed missing array declaration by dareg - std::unique_ptr glyphs_ptr(new FT_UInt[len]); - auto glyphs = glyphs_ptr.get(); - const auto endstr = str + len; - for(auto chr = str; chr != endstr; ++chr) - { - (*glyphs++) = XftCharIndex(disp, fs, *chr); - } - XftDrawGlyphs(dw->xftdraw, &(dw->xft_fgcolor), fs, pos.x, pos.y + fs->ascent, glyphs_ptr.get(), len); + nana_xft_draw_string(dw->xftdraw, &(dw->xft_fgcolor), dw->font.get(), pos, str, len); #else XFontSet fs = reinterpret_cast(dw->font->native_handle()); XFontSetExtents * ext = ::XExtentsOfFontSet(fs); diff --git a/source/paint/graphics.cpp b/source/paint/graphics.cpp index c217dd70..c622aa3f 100644 --- a/source/paint/graphics.cpp +++ b/source/paint/graphics.cpp @@ -1,7 +1,7 @@ /* * Paint Graphics Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -28,6 +28,10 @@ namespace nana { + //Forward-declarations + //These names are defined platform_abstraction.cpp + std::unique_ptr nana_xft_glyph_pixels(font_interface*, const wchar_t* str, std::size_t len); + namespace detail { font_style::font_style(unsigned weight, bool italic, bool underline, bool strike_out) : @@ -502,8 +506,8 @@ namespace paint if (!text.empty()) { - unsigned tab_pixels = impl_->handle->string.tab_length * impl_->handle->string.whitespace_pixels; #if defined(NANA_WINDOWS) + unsigned tab_pixels = impl_->handle->string.tab_length * impl_->handle->string.whitespace_pixels; int * dx = new int[text.size()]; SIZE extents; ::GetTextExtentExPoint(impl_->handle->context, text.data(), static_cast(text.size()), 0, 0, dx, &extents); @@ -516,22 +520,7 @@ namespace paint } delete[] dx; #elif defined(NANA_X11) && defined(NANA_USE_XFT) - - auto disp = nana::detail::platform_spec::instance().open_display(); - auto xft = reinterpret_cast(impl_->handle->font->native_handle()); - - XGlyphInfo extents; - for (std::size_t i = 0; i < text.size(); ++i) - { - if (text[i] != '\t') - { - FT_UInt glyphs = ::XftCharIndex(disp, xft, text[i]); - ::XftGlyphExtents(disp, xft, &glyphs, 1, &extents); - pxbuf[i] = extents.xOff; - } - else - pxbuf[i] = tab_pixels; - } + return nana_xft_glyph_pixels(impl_->handle->font.get(), text.data(), text.size()); #endif } return pxbuf; @@ -1100,7 +1089,7 @@ namespace paint ::nana::color graphics::palette(bool for_text) const { if (impl_->handle) - return static_cast(for_text ? impl_->handle->get_text_color() : impl_->handle->get_color()); + return static_cast(for_text ? impl_->handle->fgcolor_rgb : impl_->handle->bgcolor_rgb); return{}; } @@ -1132,7 +1121,7 @@ namespace paint if (impl_->handle) { #if defined(NANA_WINDOWS) - ::SetPixel(impl_->handle->context, x, y, NANA_RGB(impl_->handle->get_color())); + ::SetPixel(impl_->handle->context, x, y, impl_->handle->bgcolor_native); #elif defined(NANA_X11) Display* disp = nana::detail::platform_spec::instance().open_display(); impl_->handle->update_color(); @@ -1328,14 +1317,14 @@ namespace paint #if defined(NANA_WINDOWS) if (pos1 != pos2) { - auto prv_pen = ::SelectObject(impl_->handle->context, ::CreatePen(PS_SOLID, 1, NANA_RGB(impl_->handle->get_color()))); + auto prv_pen = ::SelectObject(impl_->handle->context, ::CreatePen(PS_SOLID, 1, impl_->handle->bgcolor_native)); ::MoveToEx(impl_->handle->context, pos1.x, pos1.y, 0); ::LineTo(impl_->handle->context, pos2.x, pos2.y); ::DeleteObject(::SelectObject(impl_->handle->context, prv_pen)); } - ::SetPixel(impl_->handle->context, pos2.x, pos2.y, NANA_RGB(impl_->handle->get_color())); + ::SetPixel(impl_->handle->context, pos2.x, pos2.y, impl_->handle->bgcolor_native); #elif defined(NANA_X11) Display* disp = nana::detail::platform_spec::instance().open_display(); impl_->handle->update_color(); @@ -1361,7 +1350,7 @@ namespace paint { if (!impl_->handle) return; #if defined(NANA_WINDOWS) - auto prv_pen = ::SelectObject(impl_->handle->context, ::CreatePen(PS_SOLID, 1, NANA_RGB(impl_->handle->get_color()))); + auto prv_pen = ::SelectObject(impl_->handle->context, ::CreatePen(PS_SOLID, 1, impl_->handle->bgcolor_native)); ::LineTo(impl_->handle->context, pos.x, pos.y); @@ -1394,7 +1383,7 @@ namespace paint { #if defined(NANA_WINDOWS) - auto brush = ::CreateSolidBrush(NANA_RGB(impl_->handle->get_color())); + auto brush = ::CreateSolidBrush(impl_->handle->bgcolor_native); ::RECT native_r = { r.x, r.y, r.right(), r.bottom()}; @@ -1519,6 +1508,7 @@ namespace paint if (impl_->changed == false) impl_->changed = true; } +#define NANA_WINDOWS_RGB(a) (((DWORD)(a) & 0xFF)<<16) | ((DWORD)(a) & 0xFF00) | (((DWORD)(a) & 0xFF0000) >> 16 ) void graphics::round_rectangle(const ::nana::rectangle& r, unsigned radius_x, unsigned radius_y, const color& clr, bool solid, const color& solid_clr) { if (impl_->handle) @@ -1528,8 +1518,8 @@ namespace paint if (solid) { - auto prv_pen = ::SelectObject(impl_->handle->context, ::CreatePen(PS_SOLID, 1, NANA_RGB(impl_->handle->get_color()))); - auto prv_brush = ::SelectObject(impl_->handle->context, ::CreateSolidBrush(NANA_RGB(solid_clr.px_color().value))); + auto prv_pen = ::SelectObject(impl_->handle->context, ::CreatePen(PS_SOLID, 1, impl_->handle->bgcolor_native)); + auto prv_brush = ::SelectObject(impl_->handle->context, ::CreateSolidBrush(NANA_WINDOWS_RGB(solid_clr.px_color().value))); ::RoundRect(impl_->handle->context, r.x, r.y, r.right(), r.bottom(), static_cast(radius_x * 2), static_cast(radius_y * 2)); @@ -1538,7 +1528,7 @@ namespace paint } else { - auto brush = ::CreateSolidBrush(NANA_RGB(impl_->handle->get_color())); + auto brush = ::CreateSolidBrush(impl_->handle->bgcolor_native); auto region = ::CreateRoundRectRgn(r.x, r.y, r.x + static_cast(r.width) + 1, r.y + static_cast(r.height) + 1, static_cast(radius_x + 1), static_cast(radius_y + 1)); diff --git a/source/paint/text_renderer.cpp b/source/paint/text_renderer.cpp index 6f5654dd..19b305a9 100644 --- a/source/paint/text_renderer.cpp +++ b/source/paint/text_renderer.cpp @@ -16,7 +16,7 @@ namespace nana auto const end = str + len; for(auto i = str; i != end; ++i) { - if(*i == '\n') + if('\n' == *i) { top += static_cast(f(top, str, i - str)); str = i + 1; @@ -26,212 +26,180 @@ namespace nana f(top, str, end - str); } - struct draw_string - { - drawable_type dw; - const int x, endpos; - align text_align; - draw_string(drawable_type dw, int x, int endpos, align ta) - : dw(dw), x(x), endpos(endpos), text_align(ta) - {} + class string_drawer + { + public: + string_drawer(graphics& graph, int left, int right, align ta, bool use_ellipsis): + graph_(graph), + left_(left), + right_(right), + text_align_(ta) + { + if (use_ellipsis) + { +#ifdef _nana_std_has_string_view + ellipsis_px_ = graph.text_extent_size(std::string_view{ "...", 3 }).width; +#else + ellipsis_px_ = graph.text_extent_size("...", 3).width; +#endif + } + } + unsigned operator()(const int top, const wchar_t * buf, std::size_t bufsize) { + auto const drawable = graph_.handle(); auto const reordered = unicode_reorder(buf, bufsize); - if (reordered.empty()) - return 0; + + unsigned return_max_height = 0; + unsigned string_px = 0; + std::vector word_metrics; + for (auto & ent : reordered) + { + auto word_sz = detail::text_extent_size(drawable, ent.begin, ent.end - ent.begin); + word_metrics.push_back(word_sz); - nana::point pos{ x, top }; - unsigned pixels = 0; + string_px += word_sz.width; + if (word_sz.height > return_max_height) + return_max_height = word_sz.height; + } - switch(text_align) + auto text_align = text_align_; + // Checks if ellipsis is enabled and the total pixels of string is larger than the space. + if (ellipsis_px_ && (static_cast(string_px) > right_ - left_)) + { + //The string should be drawn from left most point no matter what text align is. + text_align = align::left; + } + + nana::point pos{ left_, top }; + + auto wdm = word_metrics.data(); + switch (text_align) { case align::left: - for(auto & ent : reordered) + for (auto & ent : reordered) { - std::size_t len = ent.end - ent.begin; - nana::size ts = detail::text_extent_size(dw, ent.begin, len); + if (pos.x + static_cast(wdm->width) > 0) + { + if (pos.x + static_cast(wdm->width) <= right_ - static_cast(ellipsis_px_)) + { + //This word can be fully painted. + detail::draw_string(drawable, pos, ent.begin, ent.end - ent.begin); + } + else + { + //This word is painted partially. Firstly, paints the word on a dummy graphics buffer. - if(ts.height > pixels) pixels = ts.height; - - if(pos.x + static_cast(ts.width) > 0) - detail::draw_string(dw, pos, ent.begin, len); + nana::rectangle r{ nana::size{ static_cast(right_ - ellipsis_px_) - pos.x, wdm->height } }; - pos.x += static_cast(ts.width); - if(pos.x >= endpos) + nana::paint::graphics dummy({ r.width, r.height }); + dummy.typeface(graph_.typeface()); + + dummy.bitblt(r, graph_, pos); + +#ifdef _nana_std_has_string_view + dummy.string({}, { ent.begin, static_cast(ent.end - ent.begin) }, graph_.palette(true)); +#else + dummy.palette(true, graph_.palette(true)); + dummy.string({}, ent.begin, ent.end - ent.begin); +#endif + r.x = pos.x; + r.y = top; + graph_.bitblt(r, dummy); + if (ellipsis_px_) + detail::draw_string(drawable, point{ right_ - static_cast(ellipsis_px_), top }, L"...", 3); + break; + } + } + + pos.x += static_cast(wdm->width); + if (pos.x > right_ - static_cast(ellipsis_px_)) break; + + ++wdm; } break; case align::center: + pos.x = (right_ - left_ - string_px) / 2; + for (auto & ent : reordered) { - unsigned lenpx = 0; - std::unique_ptr entity_pxs(new unsigned[reordered.size()]); - - auto ent_px = entity_pxs.get(); - - for(auto & ent : reordered) - { - auto ts = detail::text_extent_size(dw, ent.begin, ent.end - ent.begin); - if(ts.height > pixels) pixels = ts.height; - lenpx += ts.width; - *ent_px++ = ts.width; - } - - pos.x += (endpos - pos.x - static_cast(lenpx))/2; - ent_px = entity_pxs.get(); - - for(auto & ent : reordered) - { - if (pos.x + static_cast(*ent_px) > 0) - detail::draw_string(dw, pos, ent.begin, ent.end - ent.begin); - - pos.x += static_cast(*ent_px++); - - if(pos.x >= endpos) - break; - } + detail::draw_string(drawable, pos, ent.begin, ent.end - ent.begin); + pos.x += (wdm++)->width; } break; case align::right: + wdm = word_metrics.data() + word_metrics.size() - 1; + pos.x = right_; + for (auto i = reordered.crbegin(); i != reordered.crend(); ++i) { - int xend = endpos; - std::swap(pos.x, xend); - for(auto i = reordered.crbegin(); i != reordered.crend(); ++i) - { - auto & ent = *i; - std::size_t len = ent.end - ent.begin; - nana::size ts = detail::text_extent_size(dw, ent.begin, len); - if(ts.height > pixels) pixels = ts.height; - - if(pos.x > xend) - { - pos.x -= static_cast(ts.width); - detail::draw_string(dw, pos, i->begin, len); - } - - if(pos.x <= xend || pos.x <= 0) - break; - } + pos.x -= (wdm--)->width; + detail::draw_string(drawable, pos, i->begin, i->end - i->begin); } break; } - return pixels; - } - }; - - struct draw_string_omitted - { - graphics & graph; - int x, endpos; - unsigned omitted_pixels; - - draw_string_omitted(graphics& graph, int x, int endpos, bool omitted) - : graph(graph), x(x), endpos(endpos) - { -#ifdef _nana_std_has_string_view - omitted_pixels = (omitted ? graph.text_extent_size(std::string_view{ "...", 3 }).width : 0); -#else - omitted_pixels = (omitted ? graph.text_extent_size("...", 3).width : 0); -#endif - if (endpos - x > static_cast(omitted_pixels)) - this->endpos -= omitted_pixels; - else - this->endpos = x; - } - - unsigned operator()(const int top, const wchar_t * buf, std::size_t bufsize) - { - drawable_type dw = graph.handle(); - ::nana::point pos{ x, top }; - unsigned pixels = 0; - - auto const reordered = unicode_reorder(buf, bufsize); - - for(auto & i : reordered) - { - std::size_t len = i.end - i.begin; - nana::size ts = detail::text_extent_size(dw, i.begin, len); - if(ts.height > pixels) pixels = ts.height; - - if(pos.x + static_cast(ts.width) <= endpos) - { - detail::draw_string(dw, pos, i.begin, len); - pos.x += static_cast(ts.width); - } - else - { - nana::rectangle r; - r.width = endpos - pos.x; - r.height = ts.height; - - nana::paint::graphics dum_graph({ r.width, r.height }); - - dum_graph.bitblt(r, graph, pos); - -#ifdef _nana_std_has_string_view - dum_graph.string({}, { i.begin, len }, graph.palette(true)); -#else - dum_graph.palette(true, graph.palette(true)); - dum_graph.string({}, i.begin, len); -#endif - - r.x = pos.x; - r.y = top; - graph.bitblt(r, dum_graph); - if(omitted_pixels) - detail::draw_string(dw, point{ endpos, top }, L"...", 3); - break; - } - } - return pixels; + return return_max_height; } + private: + graphics& graph_; + const int left_, right_; //the range of rendering area in x-axis + const align text_align_; + unsigned ellipsis_px_{ 0 }; }; struct draw_string_auto_changing_lines { graphics & graph; - int x, endpos; - align text_align; + const int left, right; + const align text_align; - draw_string_auto_changing_lines(graphics& graph, int x, int endpos, align ta) - : graph(graph), x(x), endpos(endpos), text_align(ta) + draw_string_auto_changing_lines(graphics& graph, int left, int right, align ta): + graph(graph), + left(left), + right(right), + text_align(ta) {} unsigned operator()(const int top, const wchar_t * buf, std::size_t bufsize) { - unsigned pixels = 0; + unsigned return_max_height = 0; - auto const dw = graph.handle(); - unsigned str_w = 0; + auto const drawable = graph.handle(); + unsigned string_px = 0; - std::vector ts_keeper; + std::vector word_metrics; auto const reordered = unicode_reorder(buf, bufsize); for(auto & i : reordered) { - nana::size ts = detail::text_extent_size(dw, i.begin, i.end - i.begin); - if(ts.height > pixels) pixels = ts.height; - ts_keeper.emplace_back(ts); - str_w += ts.width; + auto word_sz = detail::text_extent_size(drawable, i.begin, i.end - i.begin); + + word_metrics.emplace_back(word_sz); + string_px += word_sz.width; + + if (return_max_height < word_sz.height) + return_max_height = word_sz.height; } + const nana::size* wdm = word_metrics.data(); + //Test whether the text needs the new line. - if(x + static_cast(str_w) > endpos) + if(left + static_cast(string_px) > right) { - pixels = 0; - unsigned line_pixels = 0; - nana::point pos{ x, top }; - int orig_top = top; - auto i_ts_keeper = ts_keeper.cbegin(); + unsigned max_height = 0; + + nana::point pos{ left, top }; + const int orig_top = top; + for(auto & i : reordered) { - if(line_pixels < i_ts_keeper->height) - line_pixels = i_ts_keeper->height; + if(max_height < wdm->height) + max_height = wdm->height; - bool beyond_edge = (pos.x + static_cast(i_ts_keeper->width) > endpos); + bool beyond_edge = (pos.x + static_cast(wdm->width) > right); if(beyond_edge) { const std::size_t len = i.end - i.begin; @@ -251,10 +219,10 @@ namespace nana { auto pxbuf = pixel_buf.get(); - idx_splitted = find_splitted(idx_head, len, pos.x, endpos, pxbuf); + idx_splitted = find_splitted(idx_head, len, pos.x, right, pxbuf); if(idx_splitted == len) { - detail::draw_string(dw, pos, i.begin + idx_head, idx_splitted - idx_head); + detail::draw_string(drawable, pos, i.begin + idx_head, idx_splitted - idx_head); for(std::size_t i = idx_head; i < len; ++i) pos.x += static_cast(pxbuf[i]); @@ -264,17 +232,16 @@ namespace nana //Check the word whether it is splittable. if(splittable(i.begin, idx_splitted)) { - detail::draw_string(dw, pos, i.begin + idx_head, idx_splitted - idx_head); + detail::draw_string(drawable, pos, i.begin + idx_head, idx_splitted - idx_head); idx_head = idx_splitted; - pos.x = x; - pos.y += static_cast(line_pixels); - line_pixels = i_ts_keeper->height; + pos.x = left; + pos.y += static_cast(max_height); } else { //Search the splittable character from idx_head to idx_splitted const wchar_t * u = i.begin + idx_splitted; - const wchar_t * head = i.begin + idx_head; + const wchar_t * const head = i.begin + idx_head; for(; head < u; --u) { @@ -284,112 +251,102 @@ namespace nana if(u != head) { - detail::draw_string(dw, pos, head, u - head); + detail::draw_string(drawable, pos, head, u - head); idx_head += u - head; - pos.x = x; - pos.y += static_cast(line_pixels); - line_pixels = i_ts_keeper->height; + pos.x = left; + pos.y += static_cast(max_height); } else { u = i.begin + idx_splitted; - const wchar_t * end = i.begin + len; - for(; u < end; ++u) + for(; u < i.begin + len; ++u) { if(splittable(head, u - head)) break; } std::size_t splen = u - head; - pos.y += static_cast(line_pixels); - pos.x = x; - detail::draw_string(dw, pos, head, splen); - line_pixels = i_ts_keeper->height; + pos.y += static_cast(max_height); + pos.x = left; + detail::draw_string(drawable, pos, head, splen); for(std::size_t k = idx_head; k < idx_head + splen; ++k) pos.x += static_cast(pxbuf[k]); - if (pos.x >= endpos) + if (pos.x >= right) { - pos.x = x; - pos.y += static_cast(line_pixels); + pos.x = left; + pos.y += static_cast(wdm->height); } idx_head += splen; } } + max_height = wdm->height; }while(idx_head < len); } else { - pos.x = x; - pos.y += static_cast(line_pixels); - detail::draw_string(dw, pos, i.begin, 1); - pos.x += static_cast(i_ts_keeper->width); + pos.x = left; + pos.y += static_cast(max_height); + detail::draw_string(drawable, pos, i.begin, 1); + pos.x += static_cast(wdm->width); } - line_pixels = 0; + max_height = 0; } else { - detail::draw_string(dw, pos, i.begin, i.end - i.begin); - pos.x += static_cast(i_ts_keeper->width); + detail::draw_string(drawable, pos, i.begin, i.end - i.begin); + pos.x += static_cast(wdm->width); } - ++i_ts_keeper; + ++wdm; } - pixels = (top - orig_top) + line_pixels; + return_max_height = (top - orig_top) + max_height; } else { + point pos{ left, top }; //The text could be drawn in a line. if((align::left == text_align) || (align::center == text_align)) { - point pos{ x, top }; if(align::center == text_align) - pos.x += (endpos - x - static_cast(str_w)) / 2; - auto i_ts_keeper = ts_keeper.cbegin(); + pos.x += (right - left - static_cast(string_px)) / 2; + for(auto & ent : reordered) { - const nana::size & ts = *i_ts_keeper; + if (pos.x + static_cast(wdm->width) > 0) + detail::draw_string(drawable, pos, ent.begin, ent.end - ent.begin); - if (pos.x + static_cast(ts.width) > 0) - detail::draw_string(dw, pos, ent.begin, ent.end - ent.begin); - - pos.x += static_cast(ts.width); - ++i_ts_keeper; + pos.x += static_cast(wdm->width); + ++wdm; } } else if(align::right == text_align) { - point pos{ endpos, top }; - auto i_ts_keeper = ts_keeper.crbegin(); + pos.x = right; for(auto i = reordered.crbegin(); i != reordered.crend(); ++i) { + if (pos.x < 0) + break; + auto & ent = *i; std::size_t len = ent.end - ent.begin; - const nana::size & ts = *i_ts_keeper; - pos.x -= static_cast(ts.width); - if (pos.x >= 0) - detail::draw_string(dw, pos, ent.begin, len); - ++i_ts_keeper; + pos.x -= static_cast(wdm->width); + detail::draw_string(drawable, pos, ent.begin, len); + ++wdm; } } } - return pixels; + return return_max_height; } static std::size_t find_splitted(std::size_t begin, std::size_t end, int x, int endpos, unsigned * pxbuf) { - unsigned acc_width = 0; - for(std::size_t i = begin; i < end; ++i) + for (std::size_t i = begin; i < end; ++i) { - if(x + static_cast(acc_width + pxbuf[i]) > endpos) - { - if(i == begin) - ++i; - return i; - } - acc_width += pxbuf[i]; + if ((x += static_cast(pxbuf[i])) > endpos) + return (begin == i ? i + 1 : i); } return end; } @@ -399,12 +356,11 @@ namespace nana wchar_t ch = str[index]; if(('0' <= ch && ch <= '9') || ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z')) { - wchar_t prch; if(index) { - prch = str[index - 1]; + auto prch = str[index - 1]; if('0' <= ch && ch <= '9') - return !(('0' <= prch && prch <= '9') || (str[index - 1] == '-')); + return !(('0' <= prch && prch <= '9') || (prch == '-')); return (('z' < prch || prch < 'a') && ('Z' < prch || prch < 'A')); } @@ -418,44 +374,53 @@ namespace nana struct extent_auto_changing_lines { graphics & graph; - int x, endpos; + const int left, right; + unsigned extents; - extent_auto_changing_lines(graphics& graph, int x, int endpos) - : graph(graph), x(x), endpos(endpos), extents(0) + extent_auto_changing_lines(graphics& graph, int left, int right): + graph(graph), + left(left), + right(right), + extents(0) {} unsigned operator()(int top, const wchar_t * buf, std::size_t bufsize) { - unsigned pixels = 0; + unsigned return_max_height = 0; - drawable_type dw = graph.handle(); - unsigned str_w = 0; - std::vector ts_keeper; + auto drawable = graph.handle(); + + std::vector word_metrics; + unsigned string_px = 0; auto const reordered = unicode_reorder(buf, bufsize); for(auto & i : reordered) { - nana::size ts = detail::text_extent_size(dw, i.begin, i.end - i.begin); - ts_keeper.emplace_back(ts); - str_w += ts.width; + auto word_sz = detail::text_extent_size(drawable, i.begin, i.end - i.begin); + word_metrics.emplace_back(word_sz); + string_px += word_sz.width; + + if (return_max_height < word_sz.height) + return_max_height = word_sz.height; } - auto i_ts_keeper = ts_keeper.cbegin(); //Test whether the text needs the new line. - if(x + static_cast(str_w) > endpos) + if(left + static_cast(string_px) > right) { - unsigned line_pixels = 0; - int xpos = x; - int orig_top = top; + unsigned max_height = 0; + int xpos = left; + const int orig_top = top; + + auto wdm = word_metrics.data(); for(auto & i : reordered) { - if(line_pixels < i_ts_keeper->height) - line_pixels = i_ts_keeper->height; + if(max_height < wdm->height) + max_height = wdm->height; - bool beyond_edge = (xpos + static_cast(i_ts_keeper->width) > endpos); + bool beyond_edge = (xpos + static_cast(wdm->width) > right); if(beyond_edge) { std::size_t len = i.end - i.begin; @@ -475,21 +440,22 @@ namespace nana do { - idx_splitted = draw_string_auto_changing_lines::find_splitted(idx_head, len, xpos, endpos, pxbuf); + idx_splitted = draw_string_auto_changing_lines::find_splitted(idx_head, len, xpos, right, pxbuf); if(idx_splitted == len) { for(std::size_t i = idx_head; i < len; ++i) xpos += static_cast(pxbuf[i]); + break; } //Check the word whether it is splittable. if(draw_string_auto_changing_lines::splittable(i.begin, idx_splitted)) { idx_head = idx_splitted; - xpos = x; - top += line_pixels; - line_pixels = i_ts_keeper->height; + xpos = left; + top += max_height; + max_height = wdm->height; } else { @@ -503,14 +469,11 @@ namespace nana break; } - if(u != head) - { - idx_head += u - head; - xpos = x; - top += line_pixels; - line_pixels = i_ts_keeper->height; - } - else + xpos = left; + top += max_height; + max_height = wdm->height; + + if(u == head) { u = i.begin + idx_splitted; const wchar_t * end = i.begin + len; @@ -520,52 +483,45 @@ namespace nana break; } std::size_t splen = u - head; - top += line_pixels; - xpos = x; - line_pixels = i_ts_keeper->height; for(std::size_t k = idx_head; k < idx_head + splen; ++k) xpos += static_cast(pxbuf[k]); - if(xpos >= endpos) + if(xpos >= right) { - xpos = x; - top += line_pixels; + xpos = left; + top += max_height; } idx_head += splen; } + else + idx_head += u - head; } }while(idx_head < len); } else - xpos = x + static_cast(i_ts_keeper->width); + xpos = left + static_cast(wdm->width); - line_pixels = 0; + max_height = 0; } else - xpos += static_cast(i_ts_keeper->width); + xpos += static_cast(wdm->width); - ++i_ts_keeper; + ++wdm; } - pixels = (top - orig_top) + line_pixels; + return_max_height = (top - orig_top) + max_height; } - else - { - while(i_ts_keeper != ts_keeper.cend()) - { - const nana::size & ts = *(i_ts_keeper++); - if(ts.height > pixels) pixels = ts.height; - } - } - extents += pixels; - return pixels; + + extents += return_max_height; + return return_max_height; } }; }//end namespace helper //class text_renderer - text_renderer::text_renderer(graph_reference graph, align ta) - : graph_(graph), text_align_(ta) + text_renderer::text_renderer(graph_reference graph, align ta): + graph_(graph), + text_align_(ta) {} nana::size text_renderer::extent_size(int x, int y, const wchar_t* str, std::size_t len, unsigned restricted_pixels) const @@ -585,26 +541,25 @@ namespace nana { if (graph_) { - helper::draw_string ds(graph_.handle(), pos.x, static_cast(graph_.width()), text_align_); - helper::for_each_line(str, len, pos.y, ds); + helper::string_drawer sd{ graph_, pos.x, pos.x + static_cast(graph_.width()), text_align_, false }; + helper::for_each_line(str, len, pos.y, sd); } } - void text_renderer::render(const point& pos, const wchar_t* str, std::size_t len, unsigned restricted_pixels, bool omitted) + void text_renderer::render(const point& pos, const wchar_t* str, std::size_t len, unsigned space_pixels, mode rendering_mode) { - if (graph_) + if (graph_ && str && len && space_pixels) { - helper::draw_string_omitted dso(graph_, pos.x, pos.x + static_cast(restricted_pixels), omitted); - helper::for_each_line(str, len, pos.y, dso); - } - } - - void text_renderer::render(const point& pos, const wchar_t * str, std::size_t len, unsigned restricted_pixels) - { - if (graph_) - { - helper::draw_string_auto_changing_lines dsacl(graph_, pos.x, pos.x + static_cast(restricted_pixels), text_align_); - helper::for_each_line(str, len, pos.y, dsacl); + if (mode::truncate_letter_with_ellipsis == rendering_mode || mode::truncate_with_ellipsis == rendering_mode) + { + helper::string_drawer sd{ graph_, pos.x, pos.x + static_cast(space_pixels), text_align_, true }; + helper::for_each_line(str, len, pos.y, sd); + } + else if (mode::word_wrap == rendering_mode) + { + helper::draw_string_auto_changing_lines dsacl(graph_, pos.x, pos.x + static_cast(space_pixels), text_align_); + helper::for_each_line(str, len, pos.y, dsacl); + } } } //end class text_renderer diff --git a/source/system/dataexch.cpp b/source/system/dataexch.cpp index 8c25ed5e..bd6e498c 100644 --- a/source/system/dataexch.cpp +++ b/source/system/dataexch.cpp @@ -15,15 +15,14 @@ #include #include #include -#include #include #if defined(NANA_WINDOWS) # include #elif defined(NANA_X11) -# include "../detail/platform_spec_selector.hpp" # include -# include +# include "../detail/platform_spec_selector.hpp" +# include "../gui/detail/basic_window.hpp" #endif namespace nana{ namespace system{ @@ -71,7 +70,6 @@ namespace nana{ namespace system{ bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); HDC hDC = ::GetDC(NULL); if (::GetDIBits(hDC, (HBITMAP)g.pixmap(), 0, 1, NULL, (BITMAPINFO *)&bmi, DIB_RGB_COLORS) == 0) { - assert(false); ::ReleaseDC(NULL, hDC); return false; } @@ -109,7 +107,6 @@ namespace nana{ namespace system{ ::CloseClipboard(); } } - assert(false); ::GlobalFree(h_gmem); return false; @@ -266,8 +263,8 @@ namespace nana{ namespace system{ spec.lock_xlib(); { - internal_scope_guard isg; - detail::bedrock::core_window_t * wd = detail::bedrock::instance().focus(); + internal_scope_guard lock; + auto wd = detail::bedrock::instance().focus(); if(wd) requester = wd->root; } spec.unlock_xlib(); diff --git a/source/threads/pool.cpp b/source/threads/pool.cpp index d4b3af2f..c58f6d58 100644 --- a/source/threads/pool.cpp +++ b/source/threads/pool.cpp @@ -141,6 +141,7 @@ namespace threads ::pthread_join(thr->handle, 0); ::pthread_detach(thr->handle); #endif + delete thr; } std::lock_guard lock(mutex_); diff --git a/source/unicode_bidi.cpp b/source/unicode_bidi.cpp index 26eaf512..f71645c5 100644 --- a/source/unicode_bidi.cpp +++ b/source/unicode_bidi.cpp @@ -1,3 +1,17 @@ +/* + * Unicode Bidi-Language Implementation + * Nana C++ Library(http://www.nanapro.org) + * Copyright(C) 2003-2019 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/unicode_bidi.cpp + * @contributors: + * glavangeorge(pr#440) + + */ #include #include @@ -976,7 +990,7 @@ namespace nana return unicode_character_type::katakana; if (('A' <= ch && ch <= 'Z') || ('a' <= ch && ch <= 'z') || (0x00AA == ch || 0x00B5 == ch || 0x00BA == ch) || (0x00C0 <= ch && ch <= 0x00D6) || - (0x00D8 <= ch && ch <= 0x00F6) || (0x00F8 <= ch && ch <= 0x0236) || (0x0250 <= ch || ch <= 0x02C1)) + (0x00D8 <= ch && ch <= 0x00F6) || (0x00F8 <= ch && ch <= 0x0236) || (0x0250 <= ch && ch <= 0x02C1)) return unicode_character_type::aletter; if ('\'' == ch || 0x00AD == ch || 0x00B7 == ch || 0x05F4 == ch || 0x2019 == ch || 0x2027 == ch)