diff --git a/.gitignore b/.gitignore index 6580f35b..0972b5ae 100644 --- a/.gitignore +++ b/.gitignore @@ -39,6 +39,7 @@ lib/ *.ninja* CMakeCache.txt CMakeFiles/ +cmake-build-debug/ .idea/ cmake_install.cmake *.DS_Store diff --git a/.travis.yml b/.travis.yml index 4543e6b3..d6805b1e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,6 +35,9 @@ matrix: - libx11-dev - libxft-dev - libboost-filesystem-dev + - libboost-system-dev + - libboost-thread-dev + - libboost-chrono-dev sources: - ubuntu-toolchain-r-test @@ -56,7 +59,7 @@ matrix: - llvm-toolchain-precise before_install: - - git clone --depth=1 --branch=develop https://github.com/qPCR4vir/nana-demo.git ../nana-demo + - git clone --depth=1 --branch=hotfix-1.5 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.4/cmake-3.4.0-rc3-Linux-x86_64.sh || true @@ -84,7 +87,7 @@ script: # we are in "... nana/../nana_lib/bin/" we need "../../nana" to get the CMakeList.txt of nana. # Thus, make install will put the nana.lib in "... nana/../nana_lib/lib/" # and the includes in "... nana/../nana_lib/include/" - - cmake -G"Unix Makefiles" ../../nana -DCMAKE_INSTALL_PREFIX=.. -DNANA_CMAKE_ENABLE_JPEG=ON -DNANA_CMAKE_ENABLE_PNG=OFF -DNANA_CMAKE_BUILD_DEMOS=ON -DNANA_CMAKE_ENABLE_AUDIO=OFF -DNANA_CMAKE_FIND_BOOST_FILESYSTEM=ON -DNANA_CMAKE_BOOST_FILESYSTEM_FORCE=OFF -DNANA_CMAKE_AUTOMATIC_GUI_TESTING=ON + - cmake -G"Unix Makefiles" ../../nana -DCMAKE_INSTALL_PREFIX=.. -DNANA_CMAKE_ENABLE_JPEG=ON -DNANA_CMAKE_ENABLE_PNG=OFF -DNANA_CMAKE_ENABLE_AUDIO=OFF -DNANA_CMAKE_FIND_BOOST_FILESYSTEM=ON -DNANA_CMAKE_BOOST_FILESYSTEM_FORCE=OFF -DNANA_CMAKE_AUTOMATIC_GUI_TESTING=ON - make install - ls - cd .. diff --git a/CMakeLists.txt b/CMakeLists.txt index 7f89337e..2cdeb93d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,10 +1,12 @@ # CMake configuration for Nana # Author: Andrew Kornilov(https://github.com/ierofant) # Contributors: -# Jinhao +# Jinhao # Robert Hauck - Enable support for PNG/Freetype # Qiangqiang Wu - Add biicode support -# Ariel Vina-Rodriguez (qPCR4vir) +# Ariel Vina-Rodriguez (qPCR4vir) +# Pavel O. - fix compilation with boost::filesystem (#281) +# Frostbane - Add option for compiling a shared library (#263,#265) # # Nana uses some build systems: MS-VS solution, MAKE, bakefile, codeblock, etc. manually optimized. # In the future CMake could be the prefered, and maybe will be used to generate the others and the central nana repo @@ -30,9 +32,11 @@ option(NANA_CMAKE_LIBPNG_FROM_OS "Use libpng from operating system." ON) option(NANA_CMAKE_ENABLE_JPEG "Enable the use of JPEG" OFF) option(NANA_CMAKE_LIBJPEG_FROM_OS "Use libjpeg from operating system." ON) option(NANA_CMAKE_ENABLE_AUDIO "Enable class audio::play for PCM playback." OFF) +option(NANA_CMAKE_SHARED_LIB "Compile nana as a shared library." OFF) option(NANA_CMAKE_VERBOSE_PREPROCESSOR "Show annoying debug messages during compilation." ON) option(NANA_CMAKE_STOP_VERBOSE_PREPROCESSOR "Stop compilation after showing the annoying debug messages." OFF) option(NANA_CMAKE_AUTOMATIC_GUI_TESTING "Activate automatic GUI testing?" OFF) +option(NANA_CLION "Activate some CLion specific workarounds" OFF) # The ISO C++ File System Technical Specification (ISO-TS, or STD) is optional. # http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4100.pdf @@ -45,7 +49,7 @@ option(NANA_CMAKE_AUTOMATIC_GUI_TESTING "Activate automatic GUI testing?" OFF) # 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. -# You can change that default if you change one of the following +# You can change that default if you change one of the following # (please don't define more than one of the _XX_FORCE options): option(NANA_CMAKE_FIND_BOOST_FILESYSTEM "Search: Is Boost filesystem available?" OFF) option(NANA_CMAKE_NANA_FILESYSTEM_FORCE "Force nana filesystem over ISO and boost?" OFF) @@ -62,11 +66,12 @@ if(POLICY CMP0004) # ignore leading space cmake_policy(SET CMP0004 OLD) endif() +set(CMAKE_DEBUG_POSTFIX "_d") + ########### OS if(WIN32) add_definitions(-DWIN32) - set(CMAKE_DEBUG_POSTFIX "_d") #Global MSVC definitions. You may prefer the hand-tuned sln and projects from the nana repository. if(MSVC) option(MSVC_USE_MP "Set to ON to build nana with the /MP option (Visual Studio 2005 and above)." ON) @@ -96,8 +101,24 @@ if(WIN32) add_definitions(-DNANA_ENABLE_MINGW_STD_THREADS_WITH_MEGANZ) endif(NANA_CMAKE_ENABLE_MINGW_STD_THREADS_WITH_MEGANZ) endif(MINGW) + + if (MSVC) + set (DLLTOOL OFF) + else () + # mingw: If dlltool is found the def and lib file will be created + message (STATUS "Detecting dlltool") + find_program (DLLTOOL dlltool) + if (DLLTOOL) + message (STATUS "Found dlltool: ${DLLTOOL}") + else () + message (WARNING "dlltool not found. Skipping import library generation.") + endif (DLLTOOL) + endif (MSVC) + endif(WIN32) + + if(APPLE) add_definitions(-DAPPLE) include_directories(/opt/X11/include/) @@ -105,7 +126,7 @@ if(APPLE) set(ENABLE_AUDIO OFF) elseif(UNIX) add_definitions(-Dlinux) - message("added -D linux") + message("added -D linux") endif(APPLE) if(UNIX) @@ -121,35 +142,47 @@ endif(UNIX) ########### Compilers # # Using gcc: gcc 4.8 don't support C++14 and make_unique. You may want to update at least to 4.9. -# In Windows, the gcc which come with CLion was 4.8 from MinGW. You may want to install MinGW-w64 from the -# TDM-GCC Compiler Suite for Windows which will update you to gcc 5.1. # gcc 5.3 and 5.4 include filesytem, but you need to add the link flag: -lstdc++fs # +# In Windows, the gcc which come with CLion was 4.8 from MinGW. +# CLion was updated to MinGW with gcc 6.3 ? Allways check this in File/Settings.../toolchains +# You could install MinGW-w64 from the TDM-GCC Compiler Suite for Windows which will update you to gcc 5.1. +# It is posible to follow https://computingabdn.com/softech/mingw-howto-install-gcc-for-windows/ +# and install MinGW with gcc 7.1 with has STD_THREADS and fs, from: https://sourceforge.net/projects/mingw-w64/files/ +# +# # see at end of: https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_dynamic_or_shared.html if(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") # Clang || GNU if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++14 -Wall -g") # Clang - + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++14 -Wall -g") # Clang + else ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall -g") # GNU - + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall -g") # GNU + endif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") endif(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") # enable static linkage # GNU || CLang not MinGW if(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") # AND NOT MINGW - # set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++ -pthread") - list(APPEND NANA_LINKS -static-libgcc -static-libstdc++ -pthread) - # message("Setting NANA_LINKS to -static-libgcc -static-libstdc++ -pthread or ${NANA_LINKS}") + + if(NANA_CMAKE_SHARED_LIB) + list(APPEND NANA_LINKS -lgcc -lstdc++ -pthread) + else() + set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++ -pthread") + # message("Setting NANA_LINKS to -static-libgcc -static-libstdc++ -pthread or ${NANA_LINKS}") + endif(NANA_CMAKE_SHARED_LIB) + if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.3) - # IS_GNUCXX < 5.3 + # IS_GNUCXX < 5.3 else(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.3) - # set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lstdc++fs") # IS_GNUCXX 5.3 or more - list(APPEND NANA_LINKS -lstdc++fs) + + # set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lstdc++fs") # IS_GNUCXX 5.3 or more + list(APPEND NANA_LINKS -lstdc++fs) + endif(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.3) - + endif(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") # AND NOT MINGW @@ -163,14 +196,16 @@ endif () # Find PNG if(NANA_CMAKE_ENABLE_PNG) - add_definitions(-DNANA_ENABLE_PNG) if(NANA_CMAKE_LIBPNG_FROM_OS) find_package(PNG) if (PNG_FOUND) include_directories( ${PNG_INCLUDE_DIRS}) list(APPEND NANA_LINKS ${PNG_LIBRARIES}) - add_definitions(-DUSE_LIBPNG_FROM_OS) + add_definitions("-DNANA_ENABLE_PNG" + "-DUSE_LIBPNG_FROM_OS") endif(PNG_FOUND) + else() + add_definitions(-DNANA_ENABLE_PNG) endif(NANA_CMAKE_LIBPNG_FROM_OS) endif(NANA_CMAKE_ENABLE_PNG) @@ -182,8 +217,11 @@ if(NANA_CMAKE_ENABLE_JPEG) if (JPEG_FOUND) include_directories( ${JPEG_INCLUDE_DIR}) list(APPEND NANA_LINKS ${JPEG_LIBRARY}) - add_definitions(-DUSE_LIBJPEG_FROM_OS) + add_definitions("-DNANA_ENABLE_JPEG" + "-DUSE_LIBJPEG_FROM_OS") endif(JPEG_FOUND) + else() + add_definitions(-DNANA_ENABLE_JPEG) endif(NANA_CMAKE_LIBJPEG_FROM_OS) endif(NANA_CMAKE_ENABLE_JPEG) @@ -208,7 +246,7 @@ elseif (NANA_CMAKE_STD_FILESYSTEM_FORCE) add_definitions(-DSTD_FILESYSTEM_FORCE) elseif (NANA_CMAKE_FIND_BOOST_FILESYSTEM OR NANA_CMAKE_BOOST_FILESYSTEM_FORCE) if (NANA_CMAKE_BOOST_FILESYSTEM_FORCE) - add_definitions(-DNANA_BOOST_FILESYSTEM_FORCE) + add_definitions(-DBOOST_FILESYSTEM_FORCE) endif(NANA_CMAKE_BOOST_FILESYSTEM_FORCE) # https://cmake.org/cmake/help/git-master/module/FindBoost.html # Implicit dependencies such as Boost::filesystem requiring Boost::system will be automatically detected and satisfied, @@ -216,7 +254,7 @@ elseif (NANA_CMAKE_FIND_BOOST_FILESYSTEM OR NANA_CMAKE_BOOST_FILESYSTEM_FORCE) # If using Boost::thread, then Thread::Thread will also be added automatically. find_package(Boost COMPONENTS filesystem) if (Boost_FOUND) - add_definitions(-DNANA_BOOST_FILESYSTEM_AVAILABLE) + add_definitions(-DBOOST_FILESYSTEM_AVAILABLE) include_directories(SYSTEM "${Boost_INCLUDE_DIR}") list(APPEND NANA_LINKS ${Boost_LIBRARIES}) ###### FIRST !!!!!!!!!!!!!!!!! add is not first endif (Boost_FOUND) @@ -232,8 +270,8 @@ if(NANA_CMAKE_VERBOSE_PREPROCESSOR) add_definitions(-DVERBOSE_PREPROCESSOR) endif(NANA_CMAKE_VERBOSE_PREPROCESSOR) if(NANA_CMAKE_AUTOMATIC_GUI_TESTING) - add_definitions(-DNANA_AUTOMATIC_GUI_TESTING) - enable_testing () + add_definitions(-DNANA_AUTOMATIC_GUI_TESTING) + enable_testing () endif(NANA_CMAKE_AUTOMATIC_GUI_TESTING) @@ -266,7 +304,16 @@ foreach(subdir ${NANA_SOURCE_SUBDIRS}) # message("Subir: ${subdir}") # message("Files: ${sources}") endforeach(subdir ${NANA_SOURCE_SUBDIRS}) -add_library(${PROJECT_NAME} ${sources} ) +if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") + add_definitions(-fmax-errors=3) +endif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") + +if(NANA_CMAKE_SHARED_LIB) + add_library(${PROJECT_NAME} SHARED ${sources} ) +else() + add_library(${PROJECT_NAME} STATIC ${sources} ) +endif(NANA_CMAKE_SHARED_LIB) + target_include_directories(${PROJECT_NAME} PUBLIC ${NANA_INCLUDE_DIR}) target_link_libraries(${PROJECT_NAME} ${NANA_LINKS}) @@ -275,33 +322,79 @@ target_link_libraries(${PROJECT_NAME} ${NANA_LINKS}) # Installing: the static "nana lib" will be in DESTDIR/CMAKE_INSTALL_PREFIX/lib/ # and the includes files "include/nana/" in DESTDIR/CMAKE_INSTALL_PREFIX/include/nana/ +# unfortunatelly install() is still ignored by CLion: +# https://intellij-support.jetbrains.com/hc/en-us/community/posts/205822949-CMake-install-isn-t-supported- install(TARGETS ${PROJECT_NAME} ARCHIVE DESTINATION lib - LIBRARY DESTINATION lib) -message("The compiled Nana library will be installed in ${CMAKE_INSTALL_PREFIX}/lib") -# Install the include directories too. -if(NANA_CMAKE_INSTALL_INCLUDES) - install(DIRECTORY ${NANA_INCLUDE_DIR}/nana DESTINATION include ) - message("The Nana include files will be installed in ${CMAKE_INSTALL_PREFIX}/include") -endif(NANA_CMAKE_INSTALL_INCLUDES) + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin) + +# http://stackoverflow.com/questions/33788729/how-do-i-get-clion-to-run-an-install-target +if(NANA_CLION) # the Clion IDE don't reconize the install target + add_custom_target(install_${PROJECT_NAME} + $(MAKE) install + DEPENDS ${PROJECT_NAME} + COMMENT "Installing ${PROJECT_NAME}") +endif() set_property( TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 14 ) +if (NANA_CMAKE_SHARED_LIB) + if (WIN32) + set (CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) + + if (DLLTOOL) + #message(STATUS "CMAKE_CURRENT_BINARY_DIR = ${CMAKE_CURRENT_BINARY_DIR}") + + #generate the lib and def files needed by msvc + set_target_properties (${PROJECT_NAME} PROPERTIES OUTPUT_NAME "${PROJECT_NAME}" + ARCHIVE_OUTPUT_NAME "${PROJECT_NAME}" + LINK_FLAGS "${CMAKE_SHARED_LINKER_FLAGS_INIT} -Wl,--output-def=${CMAKE_CURRENT_BINARY_DIR}/lib${PROJECT_NAME}.def") + + add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + COMMAND echo " Generating import library" + COMMAND "${DLLTOOL}" --dllname "lib${PROJECT_NAME}.dll" + --input-def "lib${PROJECT_NAME}.def" + --output-lib "lib${PROJECT_NAME}.lib") + + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/lib${PROJECT_NAME}.def" + "${CMAKE_CURRENT_BINARY_DIR}/lib${PROJECT_NAME}.lib" DESTINATION lib) + endif () + + endif (WIN32) +endif (NANA_CMAKE_SHARED_LIB) + +message ("") + +message("The compiled Nana library will be installed in ${CMAKE_INSTALL_PREFIX}/lib") +# Install the include directories too. +if(NANA_CMAKE_INSTALL_INCLUDES) + install(DIRECTORY ${NANA_INCLUDE_DIR}/nana DESTINATION include ) + message("The Nana include files will be installed in ${CMAKE_INSTALL_PREFIX}/include") +endif(NANA_CMAKE_INSTALL_INCLUDES) + + # Just for information: -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}) -message ( "CMAKE_CXX_FLAGS = " ${CMAKE_CXX_FLAGS}) -message ( "CMAKE_EXE_LINKER_FLAGS = " ${CMAKE_EXE_LINKER_FLAGS}) -message ( "CMAKE_STATIC_LINKER_FLAGS=" ${CMAKE_STATIC_LINKER_FLAGS}) -message ( "NANA_LINKS = " ${NANA_LINKS}) -message ( "DESTDIR = " ${DESTDIR}) -message ( "CMAKE_INSTALL_PREFIX = " ${CMAKE_INSTALL_PREFIX}) -message ( "NANA_INCLUDE_DIR = " ${NANA_INCLUDE_DIR}) -message ( "CMAKE_CURRENT_SOURCE_DIR= " ${CMAKE_CURRENT_SOURCE_DIR}) -message ( "NANA_CMAKE_ENABLE_AUDIO = " ${NANA_CMAKE_ENABLE_AUDIO}) +message ("") +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}) +message ( "CMAKE_CXX_FLAGS = " ${CMAKE_CXX_FLAGS}) +message ( "CMAKE_EXE_LINKER_FLAGS = " ${CMAKE_EXE_LINKER_FLAGS}) +message ( "CMAKE_STATIC_LINKER_FLAGS = " ${CMAKE_STATIC_LINKER_FLAGS}) +message ( "NANA_LINKS = " ${NANA_LINKS}) +message ( "DESTDIR = " ${DESTDIR}) +message ( "CMAKE_INSTALL_PREFIX = " ${CMAKE_INSTALL_PREFIX}) +message ( "NANA_INCLUDE_DIR = " ${NANA_INCLUDE_DIR}) +message ( "CMAKE_CURRENT_SOURCE_DIR = " ${CMAKE_CURRENT_SOURCE_DIR}) +message ( "NANA_CMAKE_ENABLE_AUDIO = " ${NANA_CMAKE_ENABLE_AUDIO}) +message ( "NANA_CMAKE_SHARED_LIB = " ${NANA_CMAKE_SHARED_LIB}) +message ( "NANA_CLION = " ${NANA_CLION}) +message ( "CMAKE_MAKE_PROGRAM = " ${CMAKE_MAKE_PROGRAM}) + message ( "NANA_CMAKE_FIND_BOOST_FILESYSTEM = " ${NANA_CMAKE_FIND_BOOST_FILESYSTEM}) message ( "NANA_CMAKE_BOOST_FILESYSTEM_FORCE = " ${NANA_CMAKE_BOOST_FILESYSTEM_FORCE}) message ( "NANA_CMAKE_BOOST_FILESYSTEM_INCLUDE_ROOT = " ${NANA_CMAKE_BOOST_FILESYSTEM_INCLUDE_ROOT}) message ( "NANA_CMAKE_BOOST_FILESYSTEM_LIB = " ${NANA_CMAKE_BOOST_FILESYSTEM_LIB}) message ( "NANA_CMAKE_AUTOMATIC_GUI_TESTING = " ${NANA_CMAKE_AUTOMATIC_GUI_TESTING}) -message ( "NANA_CMAKE_ADD_DEF_AUTOMATIC_GUI_TESTING = " ${NANA_CMAKE_ADD_DEF_AUTOMATIC_GUI_TESTING}) \ No newline at end of file +message ( "NANA_CMAKE_ADD_DEF_AUTOMATIC_GUI_TESTING = " ${NANA_CMAKE_ADD_DEF_AUTOMATIC_GUI_TESTING}) diff --git a/build/codeblocks/nana.cbp b/build/codeblocks/nana.cbp index c598a95d..18c78755 100644 --- a/build/codeblocks/nana.cbp +++ b/build/codeblocks/nana.cbp @@ -50,6 +50,7 @@ + diff --git a/build/vc2015/nana.vcxproj b/build/vc2015/nana.vcxproj index 9dd9c3bc..3ec4cf7d 100644 --- a/build/vc2015/nana.vcxproj +++ b/build/vc2015/nana.vcxproj @@ -252,6 +252,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/vc2015/nana.vcxproj.filters b/build/vc2015/nana.vcxproj.filters index 32f7afd6..d1d7ba78 100644 --- a/build/vc2015/nana.vcxproj.filters +++ b/build/vc2015/nana.vcxproj.filters @@ -292,4 +292,74 @@ Source Files\detail + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Header Files + + + Header Files + + \ No newline at end of file diff --git a/build/vc2017/nana.vcxproj b/build/vc2017/nana.vcxproj index 818d6cf8..cc3c70dc 100644 --- a/build/vc2017/nana.vcxproj +++ b/build/vc2017/nana.vcxproj @@ -101,6 +101,7 @@ Level3 Disabled WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + MultiThreadedDebug Windows @@ -113,6 +114,7 @@ Level3 Disabled _DEBUG;_LIB;%(PreprocessorDefinitions) + MultiThreadedDebug Windows @@ -127,6 +129,7 @@ true true WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + MultiThreaded Windows @@ -143,6 +146,7 @@ true true NDEBUG;_LIB;%(PreprocessorDefinitions) + MultiThreaded Windows @@ -232,6 +236,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/vc2017/nana.vcxproj.filters b/build/vc2017/nana.vcxproj.filters index ef973d0c..10e92180 100644 --- a/build/vc2017/nana.vcxproj.filters +++ b/build/vc2017/nana.vcxproj.filters @@ -41,6 +41,15 @@ {c1cdf46a-519f-422a-947f-39e173045414} + + {d68bd89c-170f-445f-b79f-aa03c881ab6b} + + + {a5d87649-2cd1-4a8f-a1f9-7151eaf6c772} + + + {0e6a58ab-652c-45d7-b9aa-8d9f2fa80ea1} + @@ -284,4 +293,197 @@ 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\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 + + + Include + + \ No newline at end of file diff --git a/include/nana/basic_types.hpp b/include/nana/basic_types.hpp index b190ea42..a041279b 100644 --- a/include/nana/basic_types.hpp +++ b/include/nana/basic_types.hpp @@ -312,7 +312,14 @@ namespace nana /// @param lightness in range of [0, 1] color& from_hsl(double hue, double saturation, double lightness); ///< immutable alpha channel - color blend(const color& blending_color, double alpha) const; + /// Blends color + /** + * Returns a color which is blended as this * (1 - fade_rate) + blending_color * fade_rate + * @param blending_color Color to blend + * @param fade_rate Blending rate for blending_color + * @return a blended color + */ + color blend(const color& blending_color, double fade_rate) const; /// Determines whether the color is completely transparent. bool invisible() const; diff --git a/include/nana/c++defines.hpp b/include/nana/c++defines.hpp index 4c7a244e..04b5113f 100644 --- a/include/nana/c++defines.hpp +++ b/include/nana/c++defines.hpp @@ -173,6 +173,7 @@ //Assume the std::thread is not implement on MinGW //But some toolchains may implement std::thread. +// it seems that MinGW 6.3 and 7.1 have std::thread #ifdef NANA_MINGW # ifndef STD_THREAD_NOT_SUPPORTED # define STD_THREAD_NOT_SUPPORTED @@ -220,6 +221,9 @@ # if __has_include() # undef STD_FILESYSTEM_NOT_SUPPORTED # endif +# if __has_include() +# undef STD_THREAD_NOT_SUPPORTED +# endif #endif #endif // NANA_CXX_DEFINES_INCLUDED diff --git a/include/nana/filesystem/filesystem.hpp b/include/nana/filesystem/filesystem.hpp index 59020d68..067586f5 100644 --- a/include/nana/filesystem/filesystem.hpp +++ b/include/nana/filesystem/filesystem.hpp @@ -53,6 +53,7 @@ #undef NANA_USING_BOOST_FILESYSTEM #define NANA_USING_BOOST_FILESYSTEM 1 +# include # include // add boost::filesystem into std::experimental::filesystem @@ -60,6 +61,20 @@ namespace std { namespace experimental { namespace filesystem { using namespace boost::filesystem; + using file_time_type = std::chrono::time_point; + + enum class file_type { + none = boost::filesystem::file_type::status_unknown, + not_found = boost::filesystem::file_type::file_not_found, + regular = boost::filesystem::file_type::regular_file, + directory = boost::filesystem::file_type::directory_file, + symlink = boost::filesystem::file_type::symlink_file, + block = boost::filesystem::file_type::block_file, + character = boost::filesystem::file_type::character_file, + fifo = boost::filesystem::file_type::fifo_file, + socket = boost::filesystem::file_type::socket_file, + unknown = boost::filesystem::file_type::type_unknown, + }; } // filesystem } // experimental } // std diff --git a/include/nana/gui/basis.hpp b/include/nana/gui/basis.hpp index a13b3bac..2cc656ca 100644 --- a/include/nana/gui/basis.hpp +++ b/include/nana/gui/basis.hpp @@ -91,6 +91,8 @@ namespace nana substitute = 0x1A, //Ctrl+Z escape = 0x1B, space = 0x20, //Space + del = 0x7F, //Delete + os_del = del, //Deprecated //The following names are intuitive name of ASCII control codes select_all = start_of_headline, @@ -106,8 +108,8 @@ namespace nana os_ctrl = 0x11, os_pageup = 0x21, os_pagedown, os_arrow_left = 0x25, os_arrow_up, os_arrow_right, os_arrow_down, - os_insert = 0x2D, os_del , - os_end = 0x23 , os_home //Pos 1 + os_insert = 0x2D, + os_end = 0x23, os_home //Pos 1 }; }; @@ -271,6 +273,7 @@ that return a corresponding nana::appearance with predefined values. public: virtual ~caret_interface() = default; + virtual bool activated() const = 0; virtual void disable_throw() noexcept = 0; virtual void effective_range(const rectangle& range) = 0; diff --git a/include/nana/gui/detail/basic_window.hpp b/include/nana/gui/detail/basic_window.hpp index c686f889..3f90c300 100644 --- a/include/nana/gui/detail/basic_window.hpp +++ b/include/nana/gui/detail/basic_window.hpp @@ -55,6 +55,7 @@ namespace detail void dimension(const size& s) override; void visible(bool visibility) override; bool visible() const override; + bool activated() const override; private: basic_window * owner_; point position_; diff --git a/include/nana/gui/detail/native_window_interface.hpp b/include/nana/gui/detail/native_window_interface.hpp index c80d51f1..25b2fc7f 100644 --- a/include/nana/gui/detail/native_window_interface.hpp +++ b/include/nana/gui/detail/native_window_interface.hpp @@ -1,7 +1,7 @@ /* * Platform Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -66,11 +66,11 @@ namespace detail static nana::point window_position(native_window_type); static void move_window(native_window_type, int x, int y); - static void move_window(native_window_type, const rectangle&); + static bool move_window(native_window_type, const rectangle&); static void bring_top(native_window_type, bool activated); static void set_window_z_order(native_window_type, native_window_type wd_after, z_order_action action_if_no_wd_after); - static void window_size(native_window_type, const size&); + static bool window_size(native_window_type, const size&); static void get_window_rect(native_window_type, rectangle&); static void window_caption(native_window_type, const native_string_type&); static native_string_type window_caption(native_window_type); diff --git a/include/nana/gui/widgets/float_listbox.hpp b/include/nana/gui/widgets/float_listbox.hpp index 590479fd..97670410 100644 --- a/include/nana/gui/widgets/float_listbox.hpp +++ b/include/nana/gui/widgets/float_listbox.hpp @@ -38,9 +38,9 @@ namespace nana { std::vector> items; - std::size_t max_items{10}; // the number of items display. - mutable std::size_t index{::nana::npos}; // the result of the selection. - mutable bool have_selected; + std::size_t max_items{ 10 }; // the number of items display. + mutable std::size_t index{ ::nana::npos }; // the result of the selection. + mutable bool have_selected{ false }; }; class item_renderer diff --git a/include/nana/gui/widgets/picture.hpp b/include/nana/gui/widgets/picture.hpp index 9050f8be..d4805662 100644 --- a/include/nana/gui/widgets/picture.hpp +++ b/include/nana/gui/widgets/picture.hpp @@ -36,7 +36,6 @@ namespace nana void attached(widget_reference, graph_reference) override; private: void refresh(graph_reference) override; - void _m_draw_background(unsigned,unsigned); private: implement * const impl_; }; diff --git a/include/nana/gui/widgets/spinbox.hpp b/include/nana/gui/widgets/spinbox.hpp index 2ecabc78..15cbc694 100644 --- a/include/nana/gui/widgets/spinbox.hpp +++ b/include/nana/gui/widgets/spinbox.hpp @@ -61,6 +61,7 @@ namespace nana void focus(graph_reference, const arg_focus&) override; void mouse_wheel(graph_reference, const arg_wheel&) override; + void dbl_click(graph_reference, const arg_mouse&) override; void mouse_down(graph_reference, const arg_mouse&) override; void mouse_move(graph_reference, const arg_mouse&) override; void mouse_up(graph_reference, const arg_mouse& arg) override; diff --git a/include/nana/gui/widgets/textbox.hpp b/include/nana/gui/widgets/textbox.hpp index d0ac2f67..5f6dbc91 100644 --- a/include/nana/gui/widgets/textbox.hpp +++ b/include/nana/gui/widgets/textbox.hpp @@ -185,7 +185,7 @@ namespace nana /// Appends an string. If `at_caret` is `true`, the string is inserted at the position of caret, otherwise, it is appended at end of the textbox. textbox& append(const std::string& text, bool at_caret); - + textbox& append(const std::wstring& text, bool at_caret); /// Determines whether the text is line wrapped. bool line_wrapped() const; textbox& line_wrapped(bool); diff --git a/include/nana/gui/widgets/treebox.hpp b/include/nana/gui/widgets/treebox.hpp index 1ddb448d..08548dd4 100644 --- a/include/nana/gui/widgets/treebox.hpp +++ b/include/nana/gui/widgets/treebox.hpp @@ -4,10 +4,10 @@ * 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 + * (See accompanying file LICENSE or copy at * http://www.boost.org/LICENSE_1_0.txt) * - * @file: nana/gui/widgets/treebox.hpp + * @file nana/gui/widgets/treebox.hpp * @brief * The treebox organizes the nodes by a key string. * The treebox would have a vertical scrollbar if there are too many nodes @@ -344,7 +344,7 @@ namespace nana }//end namespace drawerbase /// \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/help/widgets/treebox.htm) + /// See also in [documentation](http://nanapro.org/en-us/documentation/widgets/treebox.htm) class treebox :public widget_object < category::widget_tag, drawerbase::treebox::trigger, drawerbase::treebox::treebox_events> { @@ -372,7 +372,7 @@ namespace nana /// \brief The construct that creates a widget. /// @param wd A handle to the parent window of the widget being created. /// @param r the size and position of the widget in its parent window coordinate. - /// @param visible specifying the visible after creating. + /// @param visible specifying if visible after creating. treebox(window, const nana::rectangle& = rectangle(), bool visible = true); template @@ -397,18 +397,18 @@ namespace nana /// /// The treebox automatically redraws after certain operations, but, /// under some circumstances, it is good to disable the automatic drawing mode, - /// for example, before adding nodes in a loop, disable the mode to avoiding + /// for example, before adding nodes in a loop, disable the mode avoiding /// frequent and useless refresh for better performance, and then, after /// the operations, enable the automatic redraw mode again. - /// @param bool whether to enable. - void auto_draw(bool); + /// @param enable bool whether to enable. + void auto_draw(bool enable); /// \brief Enable the checkboxs for each item of the widget. - /// @param bool indicates whether to show or hide the checkboxs. + /// @param enable bool indicates whether to show or hide the checkboxs. treebox & checkable(bool enable); - bool checkable() const; ///< Determinte whether the checkboxs are enabled. + bool checkable() const; ///< Are the checkboxs are enabled? /// Clears the contents void clear(); @@ -424,26 +424,30 @@ namespace nana void icon_erase(const ::std::string& id); - item_proxy find(const ::std::string& keypath); ///< Find an item though a specified keypath. + item_proxy find(const ::std::string& keypath); ///< Find an item through a specified keypath. - /// Inserts a new node to treebox, but if the keypath exists returns the existing node. + /// Inserts a new node to treebox, but if the keypath exists change and returns the existing node. item_proxy insert(const ::std::string& path_key, ///< specifies the node hierarchy ::std::string title ///< used for displaying ); - /// Inserts a new node to treebox, but if the keypath exists returns the existing node. + /// Inserts a new node to treebox, but if the keypath exists change and returns the existing node. item_proxy insert( item_proxy pos, ///< the parent item node const ::std::string& key, ///< specifies the new node ::std::string title ///< title used for displaying in the new node. ); - item_proxy erase(item_proxy i); ///< Removes the node at pos and return the Item proxy following the removed node + + item_proxy erase(item_proxy i); ///< Removes the node at i and return the Item proxy following the removed node void erase(const ::std::string& keypath); ///< Removes the node by the key path. ::std::string make_key_path(item_proxy i, const ::std::string& splitter) const;/// #endif diff --git a/include/nana/paint/graphics.hpp b/include/nana/paint/graphics.hpp index 8843a03f..9ce35704 100644 --- a/include/nana/paint/graphics.hpp +++ b/include/nana/paint/graphics.hpp @@ -85,12 +85,14 @@ namespace nana bool changed() const; ///< Returns true if the graphics object is operated bool empty() const; ///< Returns true if the graphics object does not refer to any resource. - operator const void*() const; + explicit operator bool() const noexcept; drawable_type handle() const; const void* pixmap() const; const void* context() const; + void swap(graphics& other) noexcept; + /// Creates a graphics/drawable resource /** * @param sz The dimension of the graphics to be requested. If sz is empty, it performs as release(). diff --git a/include/nana/std_mutex.hpp b/include/nana/std_mutex.hpp index 38c07aaf..55f486e4 100644 --- a/include/nana/std_mutex.hpp +++ b/include/nana/std_mutex.hpp @@ -14,8 +14,12 @@ #include // http://lxr.free-electrons.com/source/include/uapi/asm-generic/errno.h#L53 //#define EPROTO 71 /* Protocol error */ +#ifdef _GLIBCXX_HAS_GTHREADS +# include +#else #include #include +#endif #else #include #include diff --git a/include/nana/std_thread.hpp b/include/nana/std_thread.hpp index e1df7ef7..87db1e5a 100644 --- a/include/nana/std_thread.hpp +++ b/include/nana/std_thread.hpp @@ -6,7 +6,11 @@ #if defined(NANA_ENABLE_MINGW_STD_THREADS_WITH_MEGANZ) -#include +#ifdef _GLIBCXX_HAS_GTHREADS +# include +#else +# include +#endif #else #include namespace std diff --git a/include/nana/stdc++.hpp b/include/nana/stdc++.hpp index 4d8bbef9..fc2d6568 100644 --- a/include/nana/stdc++.hpp +++ b/include/nana/stdc++.hpp @@ -12,6 +12,9 @@ * @brief Implement the lack support of standard library. */ +#ifndef NANA_STDCXX_INCLUDED +#define NANA_STDCXX_INCLUDED + #include "c++defines.hpp" //Implement workarounds for GCC/MinGW which version is below 4.8.2 @@ -153,4 +156,6 @@ namespace std return (v < lo ? lo : (hi < v ? hi : v)); } } -#endif \ No newline at end of file +#endif + +#endif // NANA_STDCXX_INCLUDED diff --git a/source/detail/mswin/platform_spec.hpp b/source/detail/mswin/platform_spec.hpp index 30bc8b84..5eb6df45 100644 --- a/source/detail/mswin/platform_spec.hpp +++ b/source/detail/mswin/platform_spec.hpp @@ -47,16 +47,6 @@ namespace detail bool visible; }; - struct move_window - { - enum { Pos = 1, Size = 2}; - int x; - int y; - unsigned width; - unsigned height; - unsigned ignore; //determinate that pos or size would be ignored. - }; - struct map_thread { rectangle update_area; @@ -77,7 +67,6 @@ namespace detail async_set_focus, remote_flush_surface, remote_thread_destroy_window, - remote_thread_move_window, operate_caret, //wParam: 1=Destroy, 2=SetPos remote_thread_set_window_pos, remote_thread_set_window_text, diff --git a/source/detail/platform_spec_posix.cpp b/source/detail/platform_spec_posix.cpp index e495cf7d..7f825395 100644 --- a/source/detail/platform_spec_posix.cpp +++ b/source/detail/platform_spec_posix.cpp @@ -835,6 +835,19 @@ namespace detail i->second->reinstate(); i->second->pos = pos; } + auto addr = i->second; + if(addr && addr->input_context) { + XPoint spot; + XVaNestedList list; + spot.x = pos.x; + spot.y = pos.y + addr->size.height; + list = ::XVaCreateNestedList(0, XNSpotLocation, &spot, + XNForeground, 0, + XNBackground, 0, + (void *)0); + ::XSetICValues(addr->input_context, XNPreeditAttributes, list, NULL); + ::XFree(list); + } } void platform_spec::caret_visible(native_window_type wd, bool vis) diff --git a/source/filesystem/filesystem.cpp b/source/filesystem/filesystem.cpp index 4613ca7a..fa5a8cb8 100644 --- a/source/filesystem/filesystem.cpp +++ b/source/filesystem/filesystem.cpp @@ -14,7 +14,13 @@ #include #include #include -#include //put_time + +#include +#ifdef _enable_std_put_time + #include +#else + #include +#endif #if defined(NANA_WINDOWS) #include @@ -97,7 +103,13 @@ namespace nana std::string pretty_file_date(const fs::path& path) // todo: move to .cpp { 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 diff --git a/source/gui/detail/basic_window.cpp b/source/gui/detail/basic_window.cpp index 67b24139..dc072370 100644 --- a/source/gui/detail/basic_window.cpp +++ b/source/gui/detail/basic_window.cpp @@ -204,6 +204,11 @@ namespace nana { return (visible_state::invisible != visibility_); } + + bool caret::activated() const + { + return (visible_state::displayed == visibility_); + } //end class caret //struct basic_window @@ -329,24 +334,18 @@ namespace nana return false; } - const basic_window* get_child_caret(const basic_window* wd, bool this_is_a_child) - { - if (this_is_a_child && wd->annex.caret_ptr) - return wd; - - for (auto child : wd->children) - { - auto caret_wd = get_child_caret(child, true); - if (caret_wd) - return caret_wd; - } - - return nullptr; - } - const basic_window * basic_window::child_caret() const { - return get_child_caret(this, false); + for (auto child : children) { + //Only return the child who has activated caret. + if (child->annex.caret_ptr && child->annex.caret_ptr->activated()) + return child; + + auto caret = child->child_caret(); + if (caret) + return caret; + } + return nullptr; } bool basic_window::is_draw_through() const diff --git a/source/gui/detail/bedrock_pi.cpp b/source/gui/detail/bedrock_pi.cpp index ec27ac07..3422beaa 100644 --- a/source/gui/detail/bedrock_pi.cpp +++ b/source/gui/detail/bedrock_pi.cpp @@ -151,7 +151,8 @@ namespace nana arg.window_handle = reinterpret_cast(wd); if (emit(event_code::expose, wd, arg, false, get_thread_context())) { - const core_window_t * caret_wd = (wd->annex.caret_ptr ? wd : wd->child_caret()); + //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()); if (caret_wd) { if (exposed) diff --git a/source/gui/detail/bedrock_posix.cpp b/source/gui/detail/bedrock_posix.cpp index 9b0e24e0..5f4fa1fa 100644 --- a/source/gui/detail/bedrock_posix.cpp +++ b/source/gui/detail/bedrock_posix.cpp @@ -245,22 +245,31 @@ namespace detail _m_event_filter(evt_code, wd, thrd); } - if(wd->other.upd_state == core_window_t::update_state::none) - wd->other.upd_state = core_window_t::update_state::lazy; + using update_state = basic_window::update_state; + + if(wd->other.upd_state == update_state::none) + wd->other.upd_state = update_state::lazy; _m_emit_core(evt_code, wd, false, arg, bForce__EmitInternal); - //A child of wd may not be drawn if it was out of wd's range before wd resized, - //so refresh all children of wd when a resized occurs. - if(ask_update || (event_code::resized == evt_code)) + bool good_wd = false; + if(wd_manager().available(wd)) { - wd_manager().do_lazy_refresh(wd, false, (event_code::resized == evt_code)); + //A child of wd may not be drawn if it was out of wd's range before wd resized, + //so refresh all children of wd when a resized occurs. + if(ask_update || (event_code::resized == evt_code) || (update_state::refreshed == wd->other.upd_state)) + { + wd_manager().do_lazy_refresh(wd, false, (event_code::resized == evt_code)); + } + else + wd->other.upd_state = update_state::none; + + good_wd = true; } - else if(wd_manager().available(wd)) - wd->other.upd_state = core_window_t::update_state::none; + if(thrd) thrd->event_window = prev_wd; - return true; + return good_wd; } void assign_arg(arg_mouse& arg, basic_window* wd, unsigned msg, const XEvent& evt) @@ -438,8 +447,8 @@ namespace detail keysym = keyboard::os_arrow_left + (keysym - XK_Left); break; case XK_Insert: keysym = keyboard::os_insert; break; - case XK_Delete: - keysym = keyboard::os_del; break; + case XK_Delete: case XK_KP_Delete: + keysym = keyboard::del; break; case XK_Shift_L: case XK_Shift_R: //shift keysym = keyboard::os_shift; break; case XK_Control_L: case XK_Control_R: //ctrl diff --git a/source/gui/detail/bedrock_windows.cpp b/source/gui/detail/bedrock_windows.cpp index ebf32705..2d75e7d4 100644 --- a/source/gui/detail/bedrock_windows.cpp +++ b/source/gui/detail/bedrock_windows.cpp @@ -601,40 +601,6 @@ namespace detail ::HeapFree(::GetProcessHeap(), 0, stru); } return true; - case nana::detail::messages::remote_thread_move_window: - { - auto * mw = reinterpret_cast(wParam); - - ::RECT r; - ::GetWindowRect(wd, &r); - if(mw->ignore & mw->Pos) - { - mw->x = r.left; - mw->y = r.top; - } - else - { - HWND owner = ::GetWindow(wd, GW_OWNER); - if(owner) - { - ::RECT owr; - ::GetWindowRect(owner, &owr); - ::POINT pos = {owr.left, owr.top}; - ::ScreenToClient(owner, &pos); - mw->x += (owr.left - pos.x); - mw->y += (owr.top - pos.y); - } - } - - if(mw->ignore & mw->Size) - { - mw->width = r.right - r.left; - mw->height = r.bottom - r.top; - } - ::MoveWindow(wd, mw->x, mw->y, mw->width, mw->height, true); - delete mw; - } - return true; case nana::detail::messages::remote_thread_set_window_pos: ::SetWindowPos(wd, reinterpret_cast(wParam), 0, 0, 0, 0, static_cast(lParam)); return true; @@ -775,6 +741,19 @@ namespace detail if (thrd) thrd->event_window = prev_event_wd; } + //Translate OS Virtual-Key into ASCII code + wchar_t translate_virtual_key(WPARAM vkey) + { + switch (vkey) + { + case VK_DELETE: + return 127; + case VK_DECIMAL: + return 46; + } + return static_cast(vkey); + } + LRESULT CALLBACK Bedrock_WIN32_WindowProc(HWND root_window, UINT message, WPARAM wParam, LPARAM lParam) { LRESULT window_proc_value = 0; @@ -1436,7 +1415,7 @@ namespace detail arg.evt_code = event_code::key_press; arg.window_handle = reinterpret_cast(msgwnd); arg.ignore = false; - arg.key = static_cast(wParam); + arg.key = translate_virtual_key(wParam); brock.get_key_state(arg); brock.emit(event_code::key_press, msgwnd, arg, true, &context); @@ -1522,7 +1501,7 @@ namespace detail arg_keyboard arg; arg.evt_code = event_code::key_release; arg.window_handle = reinterpret_cast(msgwnd); - arg.key = static_cast(wParam); + arg.key = translate_virtual_key(wParam); brock.get_key_state(arg); arg.ignore = false; brock.emit(event_code::key_release, msgwnd, arg, true, &context); @@ -1639,18 +1618,21 @@ namespace detail _m_event_filter(evt_code, wd, thrd); } - if (wd->other.upd_state == core_window_t::update_state::none) - wd->other.upd_state = core_window_t::update_state::lazy; + using update_state = basic_window::update_state; + + if (update_state::none == wd->other.upd_state) + wd->other.upd_state = update_state::lazy; _m_emit_core(evt_code, wd, false, arg, bForce__EmitInternal); bool good_wd = false; if (wd_manager().available(wd)) { - if (ask_update) + //Ignore ask_update if update state is refreshed. + if (ask_update || (update_state::refreshed == wd->other.upd_state)) wd_manager().do_lazy_refresh(wd, false); else - wd->other.upd_state = basic_window::update_state::none; + wd->other.upd_state = update_state::none; good_wd = true; } diff --git a/source/gui/detail/native_window_interface.cpp b/source/gui/detail/native_window_interface.cpp index 821296c1..a054dba7 100644 --- a/source/gui/detail/native_window_interface.cpp +++ b/source/gui/detail/native_window_interface.cpp @@ -863,30 +863,27 @@ namespace nana{ void native_interface::move_window(native_window_type wd, int x, int y) { #if defined(NANA_WINDOWS) - if(::GetWindowThreadProcessId(reinterpret_cast(wd), 0) != ::GetCurrentThreadId()) + ::RECT r; + ::GetWindowRect(reinterpret_cast(wd), &r); + HWND owner = ::GetWindow(reinterpret_cast(wd), GW_OWNER); + if(owner) { - nana::detail::messages::move_window * mw = new nana::detail::messages::move_window; - mw->x = x; - mw->y = y; - mw->ignore = mw->Size; - ::PostMessage(reinterpret_cast(wd), nana::detail::messages::remote_thread_move_window, reinterpret_cast(mw), 0); + ::RECT owner_rect; + ::GetWindowRect(owner, &owner_rect); + ::POINT pos = {owner_rect.left, owner_rect.top}; + ::ScreenToClient(owner, &pos); + x += (owner_rect.left - pos.x); + y += (owner_rect.top - pos.y); } - else + + + if (::GetWindowThreadProcessId(reinterpret_cast(wd), 0) != ::GetCurrentThreadId()) { - ::RECT r; - ::GetWindowRect(reinterpret_cast(wd), &r); - HWND owner = ::GetWindow(reinterpret_cast(wd), GW_OWNER); - if(owner) - { - ::RECT owner_rect; - ::GetWindowRect(owner, &owner_rect); - ::POINT pos = {owner_rect.left, owner_rect.top}; - ::ScreenToClient(owner, &pos); - x += (owner_rect.left - pos.x); - y += (owner_rect.top - pos.y); - } + nana::internal_revert_guard irg; ::MoveWindow(reinterpret_cast(wd), x, y, r.right - r.left, r.bottom - r.top, true); } + else + ::MoveWindow(reinterpret_cast(wd), x, y, r.right - r.left, r.bottom - r.top, true); #elif defined(NANA_X11) Display * disp = restrict::spec.open_display(); @@ -908,41 +905,36 @@ namespace nana{ #endif } - void native_interface::move_window(native_window_type wd, const rectangle& r) + bool native_interface::move_window(native_window_type wd, const rectangle& r) { #if defined(NANA_WINDOWS) - if(::GetWindowThreadProcessId(reinterpret_cast(wd), 0) != ::GetCurrentThreadId()) + + int x = r.x; + int y = r.y; + HWND owner = ::GetWindow(reinterpret_cast(wd), GW_OWNER); + if(owner) { - auto * mw = new nana::detail::messages::move_window; - mw->x = r.x; - mw->y = r.y; - mw->width = r.width; - mw->height = r.height; - mw->ignore = 0; - ::PostMessage(reinterpret_cast(wd), nana::detail::messages::remote_thread_move_window, reinterpret_cast(mw), 0); + ::RECT owner_rect; + ::GetWindowRect(owner, &owner_rect); + ::POINT pos = {owner_rect.left, owner_rect.top}; + ::ScreenToClient(owner, &pos); + x += (owner_rect.left - pos.x); + y += (owner_rect.top - pos.y); } - else - { - int x = r.x; - int y = r.y; - HWND owner = ::GetWindow(reinterpret_cast(wd), GW_OWNER); - if(owner) - { - ::RECT owner_rect; - ::GetWindowRect(owner, &owner_rect); - ::POINT pos = {owner_rect.left, owner_rect.top}; - ::ScreenToClient(owner, &pos); - x += (owner_rect.left - pos.x); - y += (owner_rect.top - pos.y); - } - RECT client, wd_area; - ::GetClientRect(reinterpret_cast(wd), &client); - ::GetWindowRect(reinterpret_cast(wd), &wd_area); - unsigned ext_w = (wd_area.right - wd_area.left) - client.right; - unsigned ext_h = (wd_area.bottom - wd_area.top) - client.bottom; - ::MoveWindow(reinterpret_cast(wd), x, y, r.width + ext_w, r.height + ext_h, true); + RECT client, wd_area; + ::GetClientRect(reinterpret_cast(wd), &client); + ::GetWindowRect(reinterpret_cast(wd), &wd_area); + unsigned ext_w = (wd_area.right - wd_area.left) - client.right; + unsigned ext_h = (wd_area.bottom - wd_area.top) - client.bottom; + + if (::GetWindowThreadProcessId(reinterpret_cast(wd), 0) != ::GetCurrentThreadId()) + { + nana::internal_revert_guard irg; + return (FALSE != ::MoveWindow(reinterpret_cast(wd), x, y, r.width + ext_w, r.height + ext_h, true)); } + + return (FALSE != ::MoveWindow(reinterpret_cast(wd), x, y, r.width + ext_w, r.height + ext_h, true)); #elif defined(NANA_X11) Display * disp = restrict::spec.open_display(); long supplied; @@ -984,6 +976,7 @@ namespace nana{ ::XSetWMNormalHints(disp, reinterpret_cast(wd), &hints); ::XMoveResizeWindow(disp, reinterpret_cast(wd), x, y, r.width, r.height); + return true; #endif } @@ -1062,32 +1055,28 @@ namespace nana{ #endif } - void native_interface::window_size(native_window_type wd, const size& sz) + bool native_interface::window_size(native_window_type wd, const size& sz) { #if defined(NANA_WINDOWS) - if(::GetWindowThreadProcessId(reinterpret_cast(wd), 0) != ::GetCurrentThreadId()) + ::RECT r; + ::GetWindowRect(reinterpret_cast(wd), &r); + HWND owner = ::GetWindow(reinterpret_cast(wd), GW_OWNER); + HWND parent = ::GetParent(reinterpret_cast(wd)); + if(parent && (parent != owner)) { - auto * mw = new nana::detail::messages::move_window; - mw->width = sz.width; - mw->height = sz.height; - mw->ignore = mw->Pos; - ::PostMessage(reinterpret_cast(wd), nana::detail::messages::remote_thread_move_window, reinterpret_cast(mw), 0); + ::POINT pos = {r.left, r.top}; + ::ScreenToClient(parent, &pos); + r.left = pos.x; + r.top = pos.y; } - else + + if (::GetWindowThreadProcessId(reinterpret_cast(wd), 0) != ::GetCurrentThreadId()) { - ::RECT r; - ::GetWindowRect(reinterpret_cast(wd), &r); - HWND owner = ::GetWindow(reinterpret_cast(wd), GW_OWNER); - HWND parent = ::GetParent(reinterpret_cast(wd)); - if(parent && (parent != owner)) - { - ::POINT pos = {r.left, r.top}; - ::ScreenToClient(parent, &pos); - r.left = pos.x; - r.top = pos.y; - } - ::MoveWindow(reinterpret_cast(wd), r.left, r.top, static_cast(sz.width), static_cast(sz.height), true); + nana::internal_revert_guard irg; + return (FALSE != ::MoveWindow(reinterpret_cast(wd), r.left, r.top, static_cast(sz.width), static_cast(sz.height), true)); } + + return (FALSE != ::MoveWindow(reinterpret_cast(wd), r.left, r.top, static_cast(sz.width), static_cast(sz.height), true)); #elif defined(NANA_X11) auto disp = restrict::spec.open_display(); nana::detail::platform_scope_guard psg; @@ -1104,6 +1093,7 @@ namespace nana{ ::XSetWMNormalHints(disp, reinterpret_cast(wd), &hints); } ::XResizeWindow(disp, reinterpret_cast(wd), sz.width, sz.height); + return true; #endif } diff --git a/source/gui/detail/window_layout.cpp b/source/gui/detail/window_layout.cpp index 6c3b7578..27357217 100644 --- a/source/gui/detail/window_layout.cpp +++ b/source/gui/detail/window_layout.cpp @@ -149,33 +149,34 @@ namespace nana // 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) { + auto const is_wd_root = (category::flags::root == wd->other.category); wd_rectangle block; while (wd->parent) { - auto & siblings = wd->parent->children; - //It should be checked that whether the window is still a chlid of its parent. - if (siblings.size()) + auto i = std::find(wd->parent->children.cbegin(), wd->parent->children.cend(), wd); + if (i != wd->parent->children.cend()) { - auto i = &(siblings[0]); - auto *end = i + siblings.size(); - i = std::find(i, end, wd); - if (i != end) + for (++i; i != wd->parent->children.cend(); ++i) { - //find the widget that next to wd. - for (++i; i < end; ++i) + core_window_t* cover = *i; + + if (!cover->visible) + continue; + + if (is_wd_root ? + (category::flags::root == cover->other.category) + : + ((category::flags::root != cover->other.category) && (nullptr == cover->effect.bground))) { - core_window_t* cover = *i; - if ((category::flags::root != cover->other.category) && cover->visible && (nullptr == cover->effect.bground)) + if (overlap(vis_rect, rectangle{ cover->pos_root, cover->dimension }, block.r)) { - if (overlap(vis_rect, rectangle{ cover->pos_root, cover->dimension }, block.r)) - { - block.window = cover; - blocks.push_back(block); - } + block.window = cover; + blocks.push_back(block); } } } } + wd = wd->parent; } return (!blocks.empty()); diff --git a/source/gui/detail/window_manager.cpp b/source/gui/detail/window_manager.cpp index 701eb530..4bfd1900 100644 --- a/source/gui/detail/window_manager.cpp +++ b/source/gui/detail/window_manager.cpp @@ -219,8 +219,6 @@ namespace detail Key first; Value second; - key_value_rep() = default; - key_value_rep(const Key& k) : first(k), second{} { @@ -258,15 +256,7 @@ namespace detail std::vector table_; }; - //class window_manager - struct window_handle_deleter - { - void operator()(basic_window* wd) const - { - delete wd; - } - }; - + //class window_manager //struct wdm_private_impl struct window_manager::wdm_private_impl { @@ -506,7 +496,7 @@ namespace detail if (impl_->wd_register.available(owner)) { if (owner->flags.destroying) - throw std::runtime_error("the specified owner is destory"); + throw std::runtime_error("the specified owner is destoryed"); #ifndef WIDGET_FRAME_DEPRECATED native = (category::flags::frame == owner->other.category ? @@ -953,24 +943,46 @@ namespace detail if (wd->dimension == sz) return false; + //Before resiz the window, creates the new graphics + paint::graphics graph; + paint::graphics root_graph; + if (category::flags::lite_widget != wd->other.category) + { + //If allocation fails, here throws std::bad_alloc. + graph.make(sz); + graph.typeface(wd->drawer.graphics.typeface()); + if (category::flags::root == wd->other.category) + root_graph.make(sz); + } + + auto pre_sz = wd->dimension; + wd->dimension = sz; if(category::flags::lite_widget != wd->other.category) { bool graph_state = wd->drawer.graphics.empty(); - wd->drawer.graphics.make(sz); + wd->drawer.graphics.swap(graph); //It shall make a typeface_changed() call when the graphics state is changing. - //Because when a widget is created with zero-size, it may get some wrong result in typeface_changed() call + //Because when a widget is created with zero-size, it may get some wrong results in typeface_changed() call //due to the invaliable graphics object. if(graph_state != wd->drawer.graphics.empty()) wd->drawer.typeface_changed(); if(category::flags::root == wd->other.category) { - wd->root_graph->make(sz); + //wd->root_graph->make(sz); + wd->root_graph->swap(root_graph); if(false == passive) - native_interface::window_size(wd->root, sz + nana::size(wd->extra_width, wd->extra_height)); + if (!native_interface::window_size(wd->root, sz + nana::size(wd->extra_width, wd->extra_height))) + { + wd->dimension = pre_sz; + wd->drawer.graphics.swap(graph); + wd->root_graph->swap(root_graph); + wd->drawer.typeface_changed(); + return false; + } } #ifndef WIDGET_FRAME_DEPRECATED else if(category::flags::frame == wd->other.category) diff --git a/source/gui/filebox.cpp b/source/gui/filebox.cpp index c3fa7fb0..b31653cc 100644 --- a/source/gui/filebox.cpp +++ b/source/gui/filebox.cpp @@ -435,20 +435,20 @@ namespace nana nodes_.filesystem = tree_.insert("FS.ROOT", "Filesystem"); nodes_.filesystem.value(kind::filesystem); - std::vector paths; - paths.emplace_back(fs_ext::path_user().native()); - paths.emplace_back("/"); + std::vector> paths; + paths.emplace_back(fs_ext::path_user().native(), nodes_.home); + paths.emplace_back("/", nodes_.filesystem); fs::directory_iterator end; for (auto & p : paths) { - for (fs::directory_iterator i(p); i != end; ++i) + for (fs::directory_iterator i{p.first}; i != end; ++i) { auto name = i->path().filename().native(); if (!is_directory(i->status()) || (name.size() && name[0] == '.')) continue; - item_proxy node = tree_.insert(nodes_.filesystem, name, name); + item_proxy node = tree_.insert(p.second, name, name); if (false == node.empty()) { node.value(kind::filesystem); @@ -505,21 +505,18 @@ namespace nana auto fpath = i->path().native(); auto fattr = fs::status(fpath); + auto ftype = static_cast(fattr.type()); item_fs m; m.name = name; m.directory = fs::is_directory(fattr); - switch(fattr.type()) - { - case fs::file_type::not_found: - case fs::file_type::unknown: - case fs::file_type::directory: + if (ftype == fs::file_type::not_found || + ftype == fs::file_type::unknown || + ftype == fs::file_type::directory) m.bytes = 0; - break; - default: + else m.bytes = fs::file_size(fpath); - } fs_ext::modified_file_time(fpath, m.modified_time); @@ -569,28 +566,63 @@ namespace nana if(cat_path.size() && cat_path[cat_path.size() - 1] != '/') cat_path += '/'; + auto beg = head.size(); while(true) { auto pos = path.find('/', beg); auto folder = path.substr(beg, pos != path.npos ? pos - beg: path.npos); - if(folder.size() == 0) break; + + if(folder.empty()) + break; + (cat_path += folder) += '/'; (head += folder) += '/'; path_.caption(cat_path); - for(fs::directory_iterator i(head); i != end; ++i) + try { - if (is_directory(*i)) - path_.childset(i->path().filename().native(), 0); + for(fs::directory_iterator i(head); i != end; ++i) + { + if (is_directory(*i)) + path_.childset(i->path().filename().native(), 0); + } + } + catch(fs::filesystem_error&) + { + //The directory iterator may throw filesystem_error when + //the user doesn't have permission to access the directory. + + //It just loads the sub-directories + //to the category path. } if(pos == path.npos) break; beg = pos + 1; } - _m_load_path(path); - _m_list_fs(); + + try + { + _m_load_path(path); + _m_list_fs(); + } + catch(fs::filesystem_error&) + { + file_container_.clear(); + + drawing dw{ls_file_}; + dw.clear(); + dw.draw([](paint::graphics& graph){ + std::string text = "Permission denied to access the directory"; + auto txt_sz = graph.text_extent_size(text); + auto sz = graph.size(); + + graph.string({static_cast(sz.width - txt_sz.width) / 2, static_cast(sz.height - txt_sz.height) / 2}, text, colors::dark_gray); + }); + + ls_file_.clear(); + } } bool _m_filter_allowed(const std::string& name, bool is_dir, const std::string& filter, const std::vector* extension) const @@ -611,6 +643,8 @@ namespace nana void _m_list_fs() { + drawing{ls_file_}.clear(); + auto filter = filter_.caption(); ls_file_.auto_draw(false); @@ -655,13 +689,12 @@ namespace nana return; } - using file_type = fs::file_type; - fs::path fspath(fb_.addr_.filesystem + path); - auto fst = fs::status(fspath); + auto fattr = fs::status(fspath); + auto ftype = static_cast(fattr.type()); - if(fst.type() != file_type::not_found && fst.type() != file_type::none) + if(ftype != fs::file_type::not_found && ftype != fs::file_type::none) { mb<(fattr.type()); //Check if the selected name is a directory auto is_dir = fs::is_directory(fattr); @@ -771,6 +805,7 @@ namespace nana { //Add the extension, then check if it is a directory again. fattr = fs::status(tar); + ftype = static_cast(fattr.type()); is_dir = fs::is_directory(fattr); } @@ -783,7 +818,7 @@ namespace nana if(io_read_) { - if(fs::file_type::not_found == fattr.type()) + if(fs::file_type::not_found == ftype) { msgbox mb(*this, caption()); mb.icon(msgbox::icon_information); @@ -795,7 +830,7 @@ namespace nana } else { - if(fs::file_type::not_found != fattr.type()) + if(fs::file_type::not_found != ftype) { msgbox mb(*this, caption(), msgbox::yes_no); mb.icon(msgbox::icon_question); @@ -829,14 +864,28 @@ namespace nana auto child = node.append(name, name, kind::filesystem); if(!child.empty()) { - for(fs::directory_iterator u(i->path()); u != end; ++u) + //The try-catch can be eleminated by using + //directory_iterator( const std::filesystem::path& p, std::error_code& ec ) noexcept; + //in C++17 + try { - auto uname = i->path().filename().native(); - if ((!is_directory(*i)) || (uname.size() && uname[0] == '.')) - continue; + for(fs::directory_iterator u(i->path()); u != end; ++u) + { + auto uname = u->path().filename().native(); + if ((!is_directory(*u)) || (uname.size() && uname[0] == '.')) + continue; - child.append(uname, uname, kind::filesystem); - break; + child.append(uname, uname, kind::filesystem); + break; + } + } + catch(fs::filesystem_error&) + { + //The directory iterator may throw filesystem_error when + //the user doesn't have permission to access the directory. + + //Catch the error without any process, because the loop is just + //to peak whether the directory(i->path) has a sub-directory. } } } diff --git a/source/gui/place.cpp b/source/gui/place.cpp index 257e4656..f243fcf0 100644 --- a/source/gui/place.cpp +++ b/source/gui/place.cpp @@ -58,7 +58,7 @@ namespace nana { div_start, div_end, splitter, identifier, dock, fit, hfit, vfit, vert, grid, number, array, reparray, - weight, gap, margin, arrange, variable, repeated, min_px, max_px, left, right, top, bottom, undisplayed, invisible, + weight, width, height, gap, margin, arrange, variable, repeated, min_px, max_px, left, right, top, bottom, undisplayed, invisible, collapse, parameters, equal, eof, error @@ -235,15 +235,21 @@ namespace nana idstr_.assign(idstart, sp_); - if ("weight" == idstr_ || "min" == idstr_ || "max" == idstr_) + if ( "weight" == idstr_ + || "min" == idstr_ + || "max" == idstr_ + || "width" == idstr_ + || "height" == idstr_ + ) { - auto ch = idstr_[1]; + auto c3 = idstr_[2], c1 =idstr_[0]; _m_attr_number_value(); - switch (ch) + switch (c3) { - case 'e': return token::weight; - case 'i': return token::min_px; - case 'a': return token::max_px; + case 'i': return c1=='w'? token::weight : token::height; + case 'n': return token::min_px; + case 'x': return token::max_px; + case 'd': return token::width; } } else if ("dock" == idstr_) @@ -742,6 +748,7 @@ namespace nana { public: enum class kind{ arrange, vertical_arrange, grid, splitter, dock, dockpane}; + using token = place_parts::tokenizer::token; division(kind k, std::string&& n) noexcept : kind_of_division(k), @@ -1082,6 +1089,7 @@ namespace nana ::nana::rectangle field_area; number_t weight; + token weigth_type=token::weight; number_t min_px, max_px; place_parts::margin margin; @@ -2616,10 +2624,10 @@ namespace nana auto place::implement::scan_div(place_parts::tokenizer& tknizer) -> std::unique_ptr { - typedef place_parts::tokenizer::token token; + using token = place_parts::tokenizer::token ; std::unique_ptr div; - token div_type = token::eof; + token div_type = token::eof , weight_type=token::weight; auto fit = fit_policy::none; place_parts::repeated_array fit_parameters; @@ -2726,7 +2734,7 @@ namespace nana } } break; - case token::weight: case token::min_px: case token::max_px: + 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. @@ -2736,7 +2744,9 @@ namespace nana switch (tk) { - case token::weight: weight = n; break; + 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::height: weight = n; weight_type = token::height; break; case token::min_px: min_px = n; break; case token::max_px: max_px = n; break; default: break; //Useless @@ -2805,10 +2815,16 @@ namespace nana } } + token unmatch = token::width; switch (div_type) { - case token::eof: - case token::vert: + case token::eof: unmatch = token::height; // "horitontal" div + case token::vert: // "vertical" div + for (auto& ch : children) + if (ch->weigth_type == unmatch) + throw std::invalid_argument("nana.place: unmatch vertical-heigth/horizontal-width betwen 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: @@ -2837,6 +2853,7 @@ namespace nana default: throw std::invalid_argument("nana.place: invalid division type."); } + div->weigth_type = weight_type; //Requirements for min/max //1, min and max != negative diff --git a/source/gui/programming_interface.cpp b/source/gui/programming_interface.cpp index 1fd7d158..22e22cac 100644 --- a/source/gui/programming_interface.cpp +++ b/source/gui/programming_interface.cpp @@ -1183,6 +1183,13 @@ namespace API auto caret = _m_caret(); return (caret && caret->visible()); } + + bool activated() const override + { + internal_scope_guard lock; + auto caret = _m_caret(); + return (caret && caret->activated()); + } private: caret_interface* _m_caret() const { diff --git a/source/gui/widgets/float_listbox.cpp b/source/gui/widgets/float_listbox.cpp index 2b2d9180..7613b5bb 100644 --- a/source/gui/widgets/float_listbox.cpp +++ b/source/gui/widgets/float_listbox.cpp @@ -14,6 +14,7 @@ #include #include +#include namespace nana { @@ -198,16 +199,14 @@ namespace nana return widget_; } - void attach(widget* wd, nana::paint::graphics* graph) + void attach(widget& wd, nana::paint::graphics& graph) { - if(wd) - { - widget_ = wd; - wd->events().mouse_wheel.connect_unignorable([this](const arg_wheel& arg){ - scroll_items(arg.upwards); - }); - } - if(graph) graph_ = graph; + widget_ = &wd; + wd.events().mouse_wheel.connect_unignorable([this](const arg_wheel& arg){ + scroll_items(arg.upwards); + }); + + graph_ = &graph; } void detach() @@ -219,9 +218,46 @@ namespace nana { if(module_) { - std::size_t items = (module_->max_items <= module_->items.size() ? module_->max_items : module_->items.size()); - std::size_t h = items * state_.renderer->item_pixels(*graph_); - widget_->size(size{ widget_->size().width, static_cast(h + 4) }); + + auto const items = (module_->max_items <= module_->items.size() ? module_->max_items : module_->items.size()); + + rectangle list_r{ + 0, 0, + widget_->size().width, + static_cast(items * state_.renderer->item_pixels(*graph_)) + 4 + }; + + //Test if the listbox excesses the screen + + screen scr; + auto & disp = scr.from_window(*widget_); + + auto disp_r = disp.area(); + + point pos; + API::calc_screen_point(*widget_, pos); + list_r.position(pos); + + if (widget_->size().width >= disp_r.width) + { + pos.x = 0; + list_r.width = disp_r.width; + } + else if (list_r.right() > disp_r.right()) + pos.x = disp_r.right() - static_cast(list_r.width); + + if (list_r.height >= disp_r.height) + { + pos.y = 0; + list_r.height = disp_r.height; + } + else if (list_r.bottom() > disp_r.bottom()) + pos.y = disp_r.bottom() - static_cast(list_r.height); + + API::calc_window_point(API::get_owner_window(*widget_), pos); + list_r.position(pos); + + widget_->move(list_r); } } @@ -312,36 +348,37 @@ namespace nana void _m_open_scrollbar(widget_reference wd, bool v) { - if(v) + if (!v) { - if(scrollbar_.empty() && module_) - { - scrollbar_.create(wd, rectangle(static_cast(wd.size().width - 18), 2, 16, wd.size().height - 4)); - scrollbar_.amount(module_->items.size()); - scrollbar_.range(module_->max_items); - scrollbar_.value(state_.offset_y); - - auto & events = scrollbar_.events(); - events.mouse_wheel.connect([this](const arg_wheel& arg) - { - scroll_items(arg.upwards); - }); - - auto fn = [this](const arg_mouse& arg) - { - if (arg.is_left_button() && (scrollbar_.value() != state_.offset_y)) - { - state_.offset_y = static_cast(scrollbar_.value()); - draw(); - API::update_window(*widget_); - } - }; - events.mouse_move.connect(fn); - events.mouse_up.connect(fn); - } - } - else scrollbar_.close(); + return; + } + + if(scrollbar_.empty() && module_) + { + scrollbar_.create(wd, rectangle(static_cast(wd.size().width - 18), 2, 16, wd.size().height - 4)); + scrollbar_.amount(module_->items.size()); + scrollbar_.range(module_->max_items); + scrollbar_.value(state_.offset_y); + + auto & events = scrollbar_.events(); + events.mouse_wheel.connect([this](const arg_wheel& arg) + { + scroll_items(arg.upwards); + }); + + auto fn = [this](const arg_mouse& arg) + { + if (arg.is_left_button() && (scrollbar_.value() != state_.offset_y)) + { + state_.offset_y = static_cast(scrollbar_.value()); + draw(); + API::update_window(*widget_); + } + }; + events.mouse_move.connect(fn); + events.mouse_up.connect(fn); + } } private: widget * widget_{nullptr}; @@ -393,7 +430,7 @@ namespace nana void trigger::attached(widget_reference widget, graph_reference graph) { - drawer_->attach(&widget, &graph); + drawer_->attach(widget, graph); } void trigger::detached() diff --git a/source/gui/widgets/listbox.cpp b/source/gui/widgets/listbox.cpp index c49841c5..e473a080 100644 --- a/source/gui/widgets/listbox.cpp +++ b/source/gui/widgets/listbox.cpp @@ -3931,6 +3931,11 @@ namespace nana essence_->content_view.reset(new widgets::skeletons::content_view{ widget.handle() }); essence_->resize_disp_area(); + //Set the content_view wheel speed with the listbox scheme. + essence_->content_view->set_wheel_speed([this] { + return essence_->scheme_ptr->mouse_wheel.lines; + }); + essence_->content_view->events().hover_outside = [this](const point& cur_pos) { essence_->update_mouse_selection(cur_pos); }; @@ -4166,16 +4171,17 @@ namespace nana { if (item_ptr->flags.selected != sel) { - item_ptr->flags.selected = sel; - lister.emit_cs(abs_item_pos, true); - - if (item_ptr->flags.selected) + if (sel) { + //Deselects the previously selected item. lister.cancel_others_if_single_enabled(true, abs_item_pos); essence_->lister.latest_selected_abs = abs_item_pos; } else if (essence_->lister.latest_selected_abs == abs_item_pos) essence_->lister.latest_selected_abs.set_both(npos); + + item_ptr->flags.selected = sel; + lister.emit_cs(abs_item_pos, true); } } else diff --git a/source/gui/widgets/menubar.cpp b/source/gui/widgets/menubar.cpp index a83c3d86..5071c094 100644 --- a/source/gui/widgets/menubar.cpp +++ b/source/gui/widgets/menubar.cpp @@ -403,8 +403,15 @@ namespace nana ess_->state.nullify_mouse = true; auto & menu_ptr = ess_->state.menu; + + //menu_wd will be assigned with the handle of a menu window, + //It is used for checking whether the menu is closed. A menu handler + //may close the form, checking with the data member of this trigger + //is invalid, because the form is closed, the object of menubar may not exist. + window menu_wd = nullptr; if(ess_->state.menu) { + menu_wd = menu_ptr->handle(); switch(arg.key) { case keyboard::os_arrow_down: @@ -466,8 +473,11 @@ namespace nana case keyboard::os_arrow_up: case keyboard::os_arrow_down: case keyboard::enter: - if(ess_->open_menu(true)) + if (ess_->open_menu(true)) + { + menu_wd = menu_ptr->handle(); menu_ptr->goto_next(true); + } break; case keyboard::escape: if(essence::behavior::focus == ess_->state.behave) @@ -481,15 +491,21 @@ namespace nana if(index != npos) { ess_->state.active = index; - if(ess_->open_menu(true)) + if (ess_->open_menu(true)) + { + menu_wd = menu_ptr->handle(); menu_ptr->goto_next(true); + } } break; } } - refresh(graph); - API::dev::lazy_refresh(); + if (API::is_window(menu_wd)) + { + refresh(graph); + API::dev::lazy_refresh(); + } } void trigger::key_release(graph_reference graph, const arg_keyboard& arg) diff --git a/source/gui/widgets/picture.cpp b/source/gui/widgets/picture.cpp index b1e40aae..356072b9 100644 --- a/source/gui/widgets/picture.cpp +++ b/source/gui/widgets/picture.cpp @@ -29,7 +29,6 @@ namespace nana struct implement { widget* wdg_ptr{nullptr}; - paint::graphics* graph_ptr{nullptr}; std::unique_ptr measurer; struct gradual_bground_tag @@ -48,6 +47,23 @@ namespace nana std::unique_ptr bground; //If it is not a null ptr, the widget is stretchable mode bool stretchable{ false }; //If it is true, the widget is stretchable mode without changing aspect ratio. }backimg; + + void draw_background(paint::graphics& graph, const size& dimension) + { + if (!API::dev::copy_transparent_background(*wdg_ptr, graph)) + { + auto const graph_size = graph.size(); + if (dimension.width < graph_size.width || dimension.height < graph_size.height || backimg.image.alpha()) + { + if (gradual_bground.gradual_from.invisible() || gradual_bground.gradual_to.invisible()) + graph.rectangle(true, wdg_ptr->bgcolor()); + else if (gradual_bground.gradual_from == gradual_bground.gradual_to) + graph.rectangle(true, gradual_bground.gradual_from); + else + graph.gradual_rectangle(::nana::rectangle{graph_size }, gradual_bground.gradual_from, gradual_bground.gradual_to, !gradual_bground.horizontal); + } + } + } }; class content_measurer @@ -88,32 +104,36 @@ namespace nana delete impl_; } - void drawer::attached(widget_reference& wdg, graph_reference graph) + void drawer::attached(widget_reference& wdg, graph_reference) { impl_->wdg_ptr = &wdg; - impl_->graph_ptr = &graph; API::dev::set_measurer(wdg, impl_->measurer.get()); } void drawer::refresh(graph_reference graph) { - if (!graph.changed()) - return; - - auto graphsize = graph.size(); + auto const graphsize = graph.size(); auto & backimg = impl_->backimg; if (!backimg.bground) { + if (backimg.image.empty()) + { + impl_->draw_background(graph, {}); + return; + } + auto valid_area = backimg.valid_area; if (valid_area.empty()) valid_area.dimension(backimg.image.size()); + //The position where the image to be drawn. + ::nana::point pos; + if (backimg.stretchable) { - auto fit_size = fit_zoom({ valid_area.width, valid_area.height }, graphsize); - ::nana::point pos; + auto fit_size = fit_zoom(valid_area.dimension(), graphsize); if (fit_size.width != graphsize.width) { @@ -142,15 +162,12 @@ namespace nana } } - _m_draw_background(fit_size.width, fit_size.height); + impl_->draw_background(graph, fit_size); backimg.image.stretch(valid_area, graph, ::nana::rectangle{ pos, fit_size }); } else { - //The point in which position the image to be drawn. - ::nana::point pos; - switch (backimg.align_horz) { case ::nana::align::left: break; @@ -173,15 +190,14 @@ namespace nana break; } - _m_draw_background(valid_area.width, valid_area.height); + impl_->draw_background(graph, valid_area.dimension()); - if ( ! backimg.image.empty()) - backimg.image.paste(valid_area, graph, pos); + backimg.image.paste(valid_area, graph, pos); } } else { - _m_draw_background(graphsize.width, graphsize.height); + impl_->draw_background(graph, graphsize); color invalid_clr_for_call; backimg.bground->draw(graph, invalid_clr_for_call, invalid_clr_for_call, rectangle{ graphsize }, element_state::normal); @@ -189,25 +205,6 @@ namespace nana graph.setsta(); } - - void drawer::_m_draw_background(unsigned w, unsigned h) - { - auto graph = impl_->graph_ptr; - - if (graph && (!API::dev::copy_transparent_background(*impl_->wdg_ptr, *graph))) - { - if (w < graph->size().width || h < graph->size().height || impl_->backimg.image.alpha()) - { - auto & bground = impl_->gradual_bground; - if (bground.gradual_from.invisible() || bground.gradual_to.invisible()) - graph->rectangle(true, impl_->wdg_ptr->bgcolor()); - else if (bground.gradual_from == bground.gradual_to) - graph->rectangle(true, bground.gradual_from); - else - graph->gradual_rectangle(::nana::rectangle{ graph->size() }, bground.gradual_from, bground.gradual_to, !bground.horizontal); - } - } - } //end class drawer }//end namespace picture }//end namespace drawerbase @@ -233,11 +230,7 @@ namespace nana if (backimg.bground) backimg.bground->image(backimg.image, true, valid_area); - if (handle()) - { - get_drawer_trigger().impl_->graph_ptr->set_changed(); - API::refresh_window(*this); - } + API::refresh_window(*this); } void picture::align(::nana::align horz, align_v vert) @@ -252,18 +245,11 @@ namespace nana backimg.align_horz = horz; backimg.align_vert = vert; - if (handle()) - { - get_drawer_trigger().impl_->graph_ptr->set_changed(); - API::refresh_window(*this); - } + API::refresh_window(*this); } void picture::stretchable(unsigned left, unsigned top, unsigned right, unsigned bottom) { - if (!handle()) - return; - internal_scope_guard lock; auto & backimg = get_drawer_trigger().impl_->backimg; if (!backimg.bground) @@ -275,11 +261,8 @@ namespace nana backimg.bground->stretch_parts(left, top, right, bottom); backimg.stretchable = false; - if (handle()) - { - get_drawer_trigger().impl_->graph_ptr->set_changed(); - API::refresh_window(*this); - } + + API::refresh_window(*this); } void picture::stretchable(bool enables) @@ -290,11 +273,7 @@ namespace nana backimg.bground.reset(); backimg.stretchable = enables; - if (handle()) - { - get_drawer_trigger().impl_->graph_ptr->set_changed(); - API::refresh_window(*this); - } + API::refresh_window(*this); } void picture::set_gradual_background(const ::nana::color& from, const ::nana::color& to, bool horizontal) @@ -303,11 +282,8 @@ namespace nana bground.gradual_from = from; bground.gradual_to = to; bground.horizontal = horizontal; - if (handle()) - { - get_drawer_trigger().impl_->graph_ptr->set_changed(); - API::refresh_window(*this); - } + + API::refresh_window(*this); } void picture::transparent(bool enabled) diff --git a/source/gui/widgets/progress.cpp b/source/gui/widgets/progress.cpp index 20b46ddc..2c498f7c 100644 --- a/source/gui/widgets/progress.cpp +++ b/source/gui/widgets/progress.cpp @@ -91,19 +91,26 @@ namespace nana { return value_px_; } + + bool value_px_sync() + { + if (widget_) + { + auto value_px = (widget_->size().width - border_px * 2) * value_ / max_; + + if (value_px != value_px_) + { + value_px_ = value_px; + return true; + } + } + return false; + } private: void _m_try_refresh() { - if (nullptr == widget_) - return; - - auto value_px = (widget_->size().width - border_px * 2) * value_ / max_; - - if (value_px != value_px_) - { - value_px_ = value_px; + if (value_px_sync()) API::refresh_window(*widget_); - } } private: nana::progress * widget_{ nullptr }; @@ -144,6 +151,9 @@ namespace nana auto rt_bground = rt_val; if (false == progress_->unknown(nullptr)) { + //Sync the value_px otherwise the progress is incorrect when it is resized. + progress_->value_px_sync(); + rt_bground.x = static_cast(progress_->value_px()) + static_cast(border_px); rt_bground.width -= progress_->value_px(); diff --git a/source/gui/widgets/skeletons/text_editor.cpp b/source/gui/widgets/skeletons/text_editor.cpp index f39638b2..fefaf1ed 100644 --- a/source/gui/widgets/skeletons/text_editor.cpp +++ b/source/gui/widgets/skeletons/text_editor.cpp @@ -556,6 +556,7 @@ namespace nana{ namespace widgets virtual void merge_lines(std::size_t first, std::size_t second) = 0; //Calculates how many lines the specified line of text takes with a specified pixels of width. virtual void add_lines(std::size_t pos, std::size_t lines) = 0; + virtual void prepare() = 0; virtual void pre_calc_line(std::size_t line, unsigned pixels) = 0; virtual void pre_calc_lines(unsigned pixels) = 0; virtual std::size_t take_lines() const = 0; @@ -668,6 +669,12 @@ namespace nana{ namespace widgets } } + void prepare() override + { + auto const line_count = editor_.textbase().lines(); + this->sections_.resize(line_count); + } + void pre_calc_line(std::size_t pos, unsigned) override { auto const & text = editor_.textbase().getline(pos); @@ -781,6 +788,12 @@ namespace nana{ namespace widgets } } + void prepare() override + { + auto const lines = editor_.textbase().lines(); + linemtr_.resize(lines); + } + void pre_calc_line(std::size_t line, unsigned pixels) override { const string_type& lnstr = editor_.textbase().getline(line); @@ -1220,7 +1233,7 @@ namespace nana{ namespace widgets case keyboard::os_pagedown: _m_handle_move_key(arg); break; - case keyboard::os_del: + case keyboard::del: // send delete to set_accept function if (this->attr().editable && (!impl_->capacities.pred_acceptive || impl_->capacities.pred_acceptive(key))) del(); @@ -1234,7 +1247,7 @@ namespace nana{ namespace widgets void text_editor::typeface_changed() { - impl_->capacities.behavior->pre_calc_lines(width_pixels()); + _m_reset_content_size(true); } void text_editor::indent(bool enb, std::function generator) @@ -1676,7 +1689,7 @@ namespace nana{ namespace widgets impl_->try_refresh = sync_graph::refresh; //_m_put calcs the lines - _m_reset_content_size(false); + _m_reset_content_size(true); impl_->cview->sync(false); } } @@ -2886,10 +2899,14 @@ namespace nana{ namespace widgets auto text_lines = textbase().lines(); if (text_lines <= max_lines) { + impl_->capacities.behavior->prepare(); + + auto const width_px = _m_width_px(true); + std::size_t lines = 0; for (std::size_t i = 0; i < text_lines; ++i) { - impl_->capacities.behavior->pre_calc_line(i, csize.width); + impl_->capacities.behavior->pre_calc_line(i, width_px); lines += impl_->capacities.behavior->take_lines(i); if (lines > max_lines) diff --git a/source/gui/widgets/spinbox.cpp b/source/gui/widgets/spinbox.cpp index 2fad022f..41b592f3 100644 --- a/source/gui/widgets/spinbox.cpp +++ b/source/gui/widgets/spinbox.cpp @@ -560,6 +560,12 @@ namespace nana impl_->editor()->reset_caret(); API::dev::lazy_refresh(); } + + void drawer::dbl_click(graph_reference, const arg_mouse& arg) + { + if (impl_->mouse_button(arg, true)) + API::dev::lazy_refresh(); + } void drawer::mouse_down(graph_reference, const arg_mouse& arg) { @@ -599,7 +605,10 @@ namespace nana { impl_->editor()->respond_char(arg); if (impl_->editor()->try_refresh()) + { + impl_->draw_spins(); API::dev::lazy_refresh(); + } } void drawer::resized(graph_reference, const arg_resized&) diff --git a/source/gui/widgets/textbox.cpp b/source/gui/widgets/textbox.cpp index e29b0d88..de6e2dd9 100644 --- a/source/gui/widgets/textbox.cpp +++ b/source/gui/widgets/textbox.cpp @@ -435,7 +435,23 @@ namespace drawerbase { } return *this; } + //a native wstring version textbox::append + textbox& textbox::append(const std::wstring& text, bool at_caret) + { + internal_scope_guard lock; + auto editor = get_drawer_trigger().editor(); + if(editor) + { + if(at_caret == false) + editor->move_caret_end(false); + editor->put(text); + + editor->try_refresh(); + API::update_window(this->handle()); + } + return *this; + } /// Determine wheter the text is auto-line changed. bool textbox::line_wrapped() const { diff --git a/source/gui/widgets/treebox.cpp b/source/gui/widgets/treebox.cpp index 906bd969..88468861 100644 --- a/source/gui/widgets/treebox.cpp +++ b/source/gui/widgets/treebox.cpp @@ -1825,7 +1825,6 @@ namespace nana item_locator nl(impl_, xpos, arg.pos.x, arg.pos.y); impl_->attr.tree_cont.for_each(shape.first, nl); - bool has_redraw = false; auto & node_state = impl_->node_state; node_state.pressed_node = nl.node(); @@ -1834,21 +1833,17 @@ namespace nana { if(impl_->set_expanded(node_state.pressed_node, !node_state.pressed_node->value.second.expanded)) impl_->make_adjust(node_state.pressed_node, 0); - - has_redraw = true; //btw, don't select the node } - - if ((!has_redraw) && (node_state.selected != node_state.pressed_node)) + else if (node_state.selected != node_state.pressed_node) { impl_->set_selected(node_state.pressed_node); - has_redraw = true; } + else + return; - if(has_redraw) - { - impl_->draw(true); - API::dev::lazy_refresh(); - } + + impl_->draw(true); + API::dev::lazy_refresh(); } void trigger::mouse_up(graph_reference, const arg_mouse& arg) diff --git a/source/gui/wvl.cpp b/source/gui/wvl.cpp index 820a8ba7..610484cd 100644 --- a/source/gui/wvl.cpp +++ b/source/gui/wvl.cpp @@ -15,7 +15,12 @@ #include #include #include -#include + +#ifdef STD_THREAD_NOT_SUPPORTED +# include +#else +# include +#endif //#define NANA_AUTOMATIC_GUI_TESTING namespace nana diff --git a/source/paint/graphics.cpp b/source/paint/graphics.cpp index b7dd3426..62f74016 100644 --- a/source/paint/graphics.cpp +++ b/source/paint/graphics.cpp @@ -240,12 +240,16 @@ namespace paint graphics::graphics(graphics&& other) : impl_(std::move(other.impl_)) { + other.impl_.reset(new implementation); } graphics& graphics::operator=(graphics&& other) { if (this != &other) + { impl_ = std::move(other.impl_); + other.impl_.reset(new implementation); + } return *this; } @@ -265,9 +269,9 @@ namespace paint return (!impl_->handle); } - graphics::operator const void *() const + graphics::operator bool() const noexcept { - return impl_->handle; + return (impl_->handle != nullptr); } drawable_type graphics::handle() const @@ -287,9 +291,15 @@ namespace paint return (impl_->handle ? impl_->handle->context : nullptr); } + void graphics::swap(graphics& other) noexcept + { + if (context() != other.context()) + impl_.swap(other.impl_); + } + void graphics::make(const ::nana::size& sz) { - if(impl_->handle == nullptr || impl_->size != sz) + if (impl_->handle == nullptr || impl_->size != sz) { if (sz.empty()) { @@ -298,9 +308,10 @@ namespace paint } //The object will be delete while dwptr_ is performing a release. - drawable_type dw = new nana::detail::drawable_impl_type; + std::shared_ptr dw{ new nana::detail::drawable_impl_type, detail::drawable_deleter{} }; + //Reuse the old font - if(impl_->platform_drawable) + if (impl_->platform_drawable) { drawable_type reuse = impl_->platform_drawable.get(); dw->font = reuse->font; @@ -310,8 +321,13 @@ namespace paint dw->font = impl_->font_shadow.impl_->real_font; #if defined(NANA_WINDOWS) - HDC hdc = ::GetDC(0); + HDC hdc = ::GetDC(nullptr); HDC cdc = ::CreateCompatibleDC(hdc); + if (nullptr == cdc) + { + ::ReleaseDC(nullptr, hdc); + throw std::bad_alloc{}; + } BITMAPINFO bmi; bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); @@ -324,34 +340,60 @@ namespace paint HBITMAP bmp = ::CreateDIBSection(cdc, &bmi, DIB_RGB_COLORS, reinterpret_cast(&(dw->pixbuf_ptr)), 0, 0); - if(bmp) - { - ::DeleteObject((HBITMAP)::SelectObject(cdc, bmp)); - ::DeleteObject(::SelectObject(cdc, dw->font->native_handle())); - - dw->context = cdc; - dw->pixmap = bmp; - ::SetBkMode(cdc, TRANSPARENT); - } - else + if (nullptr == bmp) { ::DeleteDC(cdc); - delete dw; - dw = nullptr; - release(); + ::ReleaseDC(nullptr, hdc); + throw std::bad_alloc{}; } + ::DeleteObject((HBITMAP)::SelectObject(cdc, bmp)); + ::DeleteObject(::SelectObject(cdc, dw->font->native_handle())); + + dw->context = cdc; + dw->pixmap = bmp; + ::SetBkMode(cdc, TRANSPARENT); + ::ReleaseDC(0, hdc); #elif defined(NANA_X11) auto & spec = nana::detail::platform_spec::instance(); - Display* disp = spec.open_display(); - int screen = DefaultScreen(disp); - Window root = ::XRootWindow(disp, screen); - dw->pixmap = ::XCreatePixmap(disp, root, sz.width, sz.height, DefaultDepth(disp, screen)); - dw->context = ::XCreateGC(disp, dw->pixmap, 0, 0); - #if defined(NANA_USE_XFT) - dw->xftdraw = ::XftDrawCreate(disp, dw->pixmap, spec.screen_visual(), spec.colormap()); - #endif + { + nana::detail::platform_scope_guard psg; + + spec.set_error_handler(); + + Display* disp = spec.open_display(); + int screen = DefaultScreen(disp); + Window root = ::XRootWindow(disp, screen); + auto pixmap = ::XCreatePixmap(disp, root, sz.width, sz.height, DefaultDepth(disp, screen)); + if(spec.error_code) + { + spec.rev_error_handler(); + throw std::bad_alloc(); + } + auto context = ::XCreateGC(disp, pixmap, 0, 0); + if (spec.error_code) + { + ::XFreePixmap(disp, pixmap); + spec.rev_error_handler(); + throw std::bad_alloc(); + } +# if defined(NANA_USE_XFT) + auto xftdraw = ::XftDrawCreate(disp, pixmap, spec.screen_visual(), spec.colormap()); + if (spec.error_code) + { + ::XFreeGC(disp, context); + ::XFreePixmap(disp, pixmap); + + spec.rev_error_handler(); + throw std::bad_alloc(); + } + + dw->xftdraw = xftdraw; +# endif + dw->pixmap = pixmap; + dw->context = context; + } #endif if(dw) { @@ -362,8 +404,8 @@ namespace paint #else dw->update_text_color(); #endif - impl_->platform_drawable.reset(dw, detail::drawable_deleter{}); - impl_->handle = dw; + impl_->platform_drawable = dw; + impl_->handle = dw.get(); impl_->size = sz; impl_->handle->string.tab_pixels = detail::raw_text_extent_size(impl_->handle, L"\t", 1).width;