diff --git a/.gitignore b/.gitignore index 0972b5ae..6bd33ab0 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,8 @@ bii/cmake/* bii/deps/* build/vc2013-bkl/* build/makefile-bkl/* +build/compile_commands.json +build/install_manifest.txt *.obj *.exe *.pdb @@ -44,5 +46,6 @@ cmake-build-debug/ cmake_install.cmake *.DS_Store -*.db -*.opendb +*.db +*.opendb +.vscode diff --git a/.travis.yml b/.travis.yml index d6805b1e..c7b02176 100644 --- a/.travis.yml +++ b/.travis.yml @@ -59,7 +59,7 @@ matrix: - llvm-toolchain-precise before_install: - - git clone --depth=1 --branch=hotfix-1.5 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.4/cmake-3.4.0-rc3-Linux-x86_64.sh || true @@ -87,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_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_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 - make install - ls - cd .. diff --git a/CMakeLists.txt b/CMakeLists.txt index 2cdeb93d..1ae8abff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -# CMake configuration for Nana +# CMake configuration for Nana # Author: Andrew Kornilov(https://github.com/ierofant) # Contributors: # Jinhao @@ -59,20 +59,14 @@ option(NANA_CMAKE_BOOST_FILESYSTEM_FORCE "Force use of Boost filesystem if avail ########### Compatibility with CMake 3.1 if(POLICY CMP0054) # http://www.cmake.org/cmake/help/v3.1/policy/CMP0054.html - cmake_policy(SET CMP0054 OLD) + cmake_policy(SET CMP0054 NEW) endif() -if(POLICY CMP0004) # ignore leading space - # http://www.cmake.org/cmake/help/v3.0/policy/CMP0004.html - cmake_policy(SET CMP0004 OLD) -endif() - -set(CMAKE_DEBUG_POSTFIX "_d") ########### OS if(WIN32) add_definitions(-DWIN32) - #Global MSVC definitions. You may prefer the hand-tuned sln and projects from the nana repository. + # 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) option(MSVC_USE_STATIC_RUNTIME "Set to ON to build nana with the /MT(d) option." ON) @@ -80,7 +74,7 @@ if(WIN32) # Change the MSVC Compiler flags if(MSVC_USE_MP) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") - endif(MSVC_USE_MP) + endif() if(MSVC_USE_STATIC_RUNTIME) foreach(flag @@ -92,32 +86,26 @@ if(WIN32) string(REGEX REPLACE "/MD" "/MT" ${flag} "${${flag}}") endif() endforeach() - endif(MSVC_USE_STATIC_RUNTIME) - endif(MSVC) + endif() + endif() if(MINGW) if(NANA_CMAKE_ENABLE_MINGW_STD_THREADS_WITH_MEGANZ) add_definitions(-DSTD_THREAD_NOT_SUPPORTED) add_definitions(-DNANA_ENABLE_MINGW_STD_THREADS_WITH_MEGANZ) - endif(NANA_CMAKE_ENABLE_MINGW_STD_THREADS_WITH_MEGANZ) - endif(MINGW) + endif() + endif() - if (MSVC) - set (DLLTOOL OFF) - else () + 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(NOT DLLTOOL) + message(WARNING "dlltool not found. Skipping import library generation.") + endif() + endif() +endif() if(APPLE) add_definitions(-DAPPLE) @@ -126,17 +114,16 @@ if(APPLE) set(ENABLE_AUDIO OFF) elseif(UNIX) add_definitions(-Dlinux) - message("added -D linux") -endif(APPLE) +endif() if(UNIX) list(APPEND NANA_LINKS -lX11) - find_package(Freetype) - if (FREETYPE_FOUND) + include(FindFreetype) + if(FREETYPE_FOUND) include_directories( ${FREETYPE_INCLUDE_DIRS}) - list(APPEND NANA_LINKS -lXft) - endif(FREETYPE_FOUND) -endif(UNIX) + list(APPEND NANA_LINKS -lXft -lfontconfig) + endif() +endif() ########### Compilers @@ -152,44 +139,31 @@ endif(UNIX) # # # 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 - - else ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") - 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 +# +if(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") + if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") + set(CMAKE_CXX_FLAGS "-std=gnu++14 -Wall") + else() + set(CMAKE_CXX_FLAGS "-std=c++14 -Wall") + endif() +endif() +if(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") 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) + set(CMAKE_EXE_LINKER_FLAGS "-static -pthread") + set(CMAKE_EXE_LINKER_FLAGS_RELEASE "-s") + endif() - if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 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 + if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.3) list(APPEND NANA_LINKS -lstdc++fs) + endif() +endif() - endif(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.3) - -endif(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") # AND NOT MINGW - - -if (APPLE AND "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") # APPLE Clang - # set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libstdc++") - list(APPEND NANA_LINKS -stdlib=libstdc++) -endif () +if(APPLE AND "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") + list(APPEND NANA_LINKS -stdlib=libstdc++) +endif() ############# Optional libraries @@ -198,69 +172,67 @@ endif () if(NANA_CMAKE_ENABLE_PNG) if(NANA_CMAKE_LIBPNG_FROM_OS) find_package(PNG) - if (PNG_FOUND) - include_directories( ${PNG_INCLUDE_DIRS}) + if(PNG_FOUND) + include_directories(${PNG_INCLUDE_DIRS}) list(APPEND NANA_LINKS ${PNG_LIBRARIES}) - add_definitions("-DNANA_ENABLE_PNG" - "-DUSE_LIBPNG_FROM_OS") - endif(PNG_FOUND) + add_definitions(-DNANA_ENABLE_PNG -DUSE_LIBPNG_FROM_OS) + endif() else() add_definitions(-DNANA_ENABLE_PNG) - endif(NANA_CMAKE_LIBPNG_FROM_OS) -endif(NANA_CMAKE_ENABLE_PNG) + endif() +endif() # Find JPEG if(NANA_CMAKE_ENABLE_JPEG) add_definitions(-DNANA_ENABLE_JPEG) if(NANA_CMAKE_LIBJPEG_FROM_OS) find_package(JPEG) - if (JPEG_FOUND) + if(JPEG_FOUND) include_directories( ${JPEG_INCLUDE_DIR}) list(APPEND NANA_LINKS ${JPEG_LIBRARY}) - add_definitions("-DNANA_ENABLE_JPEG" - "-DUSE_LIBJPEG_FROM_OS") - endif(JPEG_FOUND) + add_definitions(-DNANA_ENABLE_JPEG -DUSE_LIBJPEG_FROM_OS) + endif() else() add_definitions(-DNANA_ENABLE_JPEG) - endif(NANA_CMAKE_LIBJPEG_FROM_OS) -endif(NANA_CMAKE_ENABLE_JPEG) + endif() +endif() # Find ASOUND if(NANA_CMAKE_ENABLE_AUDIO) add_definitions(-DNANA_ENABLE_AUDIO) if(UNIX) find_package(ASOUND) - if (ASOUND_FOUND) - include_directories( ${ASOUND_INCLUDE_DIRS}) + if(ASOUND_FOUND) + include_directories(${ASOUND_INCLUDE_DIRS}) list(APPEND NANA_LINKS -lasound) - else(ASOUND_FOUND) + else() message(FATAL_ERROR "libasound is not found") - endif(ASOUND_FOUND) - endif(UNIX) -endif(NANA_CMAKE_ENABLE_AUDIO) + endif() + endif() +endif() # Find/Select filesystem -if (NANA_CMAKE_NANA_FILESYSTEM_FORCE) +if(NANA_CMAKE_NANA_FILESYSTEM_FORCE) add_definitions(-DNANA_FILESYSTEM_FORCE) -elseif (NANA_CMAKE_STD_FILESYSTEM_FORCE) +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) +elseif(NANA_CMAKE_FIND_BOOST_FILESYSTEM OR NANA_CMAKE_BOOST_FILESYSTEM_FORCE) + if(NANA_CMAKE_BOOST_FILESYSTEM_FORCE) add_definitions(-DBOOST_FILESYSTEM_FORCE) - endif(NANA_CMAKE_BOOST_FILESYSTEM_FORCE) + endif() # 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, # even if system is not specified when using find_package and if Boost::system is not added to target_link_libraries. # If using Boost::thread, then Thread::Thread will also be added automatically. find_package(Boost COMPONENTS filesystem) - if (Boost_FOUND) + if(Boost_FOUND) 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) + list(APPEND NANA_LINKS ${Boost_LIBRARIES}) + endif() set(Boost_USE_STATIC_LIBS ON) - set(Boost_USE_STATIC_RUNTIME ON) # ?? -endif (NANA_CMAKE_NANA_FILESYSTEM_FORCE) + set(Boost_USE_STATIC_RUNTIME ON) +endif() ######## Nana options @@ -268,11 +240,11 @@ endif (NANA_CMAKE_NANA_FILESYSTEM_FORCE) add_definitions(-DNANA_IGNORE_CONF) if(NANA_CMAKE_VERBOSE_PREPROCESSOR) add_definitions(-DVERBOSE_PREPROCESSOR) -endif(NANA_CMAKE_VERBOSE_PREPROCESSOR) +endif() if(NANA_CMAKE_AUTOMATIC_GUI_TESTING) - add_definitions(-DNANA_AUTOMATIC_GUI_TESTING) - enable_testing () -endif(NANA_CMAKE_AUTOMATIC_GUI_TESTING) + add_definitions(-DNANA_AUTOMATIC_GUI_TESTING) + enable_testing() +endif() ####################### Main setting of Nana sources, targets and install @@ -280,42 +252,44 @@ endif(NANA_CMAKE_AUTOMATIC_GUI_TESTING) set(NANA_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/source) set(NANA_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include) # collect all source sub-directories in a list to avoid duplication here -set(NANA_SOURCE_SUBDIRS /. - /detail - /filesystem - /gui - /gui/detail - /gui/widgets - /gui/widgets/skeletons - /paint - /paint/detail - /system - /threads ) +set(NANA_SOURCE_SUBDIRS /. + /detail + /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 ) -endif(NANA_CMAKE_ENABLE_AUDIO) + list(APPEND NANA_SOURCE_SUBDIRS /audio + /audio/detail + ) +endif() # collect all source files in the source-sub-dir # To show .h files in Visual Studio, add them to the list of sources in add_executable / add_library # and Use SOURCE_GROUP if all your sources are in the same directory foreach(subdir ${NANA_SOURCE_SUBDIRS}) - aux_source_directory(${NANA_SOURCE_DIR}${subdir} sources) - # message("Subir: ${subdir}") # message("Files: ${sources}") -endforeach(subdir ${NANA_SOURCE_SUBDIRS}) + aux_source_directory(${NANA_SOURCE_DIR}${subdir} SOURCES) +endforeach() -if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") +if("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") add_definitions(-fmax-errors=3) -endif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") +endif() + +set(CMAKE_DEBUG_POSTFIX "_d") if(NANA_CMAKE_SHARED_LIB) - add_library(${PROJECT_NAME} SHARED ${sources} ) + add_library(${PROJECT_NAME} SHARED ${SOURCES}) else() - add_library(${PROJECT_NAME} STATIC ${sources} ) -endif(NANA_CMAKE_SHARED_LIB) + add_library(${PROJECT_NAME} STATIC ${SOURCES}) +endif() target_include_directories(${PROJECT_NAME} PUBLIC ${NANA_INCLUDE_DIR}) -target_link_libraries(${PROJECT_NAME} ${NANA_LINKS}) +target_link_libraries(${PROJECT_NAME} ${NANA_LINKS}) # Headers: use INCLUDE_DIRECTORIES # Libraries: use FIND_LIBRARY and link with the result of it (try to avoid LINK_DIRECTORIES) @@ -324,27 +298,22 @@ target_link_libraries(${PROJECT_NAME} ${NANA_LINKS}) # 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 - RUNTIME DESTINATION bin) +install(TARGETS ${PROJECT_NAME} ARCHIVE DESTINATION lib + 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}") + $(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}") - +if(NANA_CMAKE_SHARED_LIB) + if(WIN32) + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) + if(DLLTOOL) #generate the lib and def files needed by msvc set_target_properties (${PROJECT_NAME} PROPERTIES OUTPUT_NAME "${PROJECT_NAME}" ARCHIVE_OUTPUT_NAME "${PROJECT_NAME}" @@ -359,42 +328,40 @@ if (NANA_CMAKE_SHARED_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 ("") + endif() + endif() +endif() +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 ) + 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) +endif() # Just for information: -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("") +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}) +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}) diff --git a/include/nana/c++defines.hpp b/include/nana/c++defines.hpp index 04b5113f..ff4cf176 100644 --- a/include/nana/c++defines.hpp +++ b/include/nana/c++defines.hpp @@ -1,7 +1,7 @@ /** * Predefined Symbols for C++ * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2016-2017 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2016-2018 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -171,9 +171,9 @@ #endif #endif -//Assume the std::thread is not implement on MinGW +//Assume the std::thread is not implemented on MinGW, +//unless it was compiled with POSIX threading support. //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 @@ -221,8 +221,11 @@ # if __has_include() # undef STD_FILESYSTEM_NOT_SUPPORTED # endif -# if __has_include() -# undef STD_THREAD_NOT_SUPPORTED +# if __has_include() +# if !(defined(NANA_MINGW) && !defined(_GLIBCXX_HAS_GTHREADS)) +//See the comment above regarding MinGW's threading support +# undef STD_THREAD_NOT_SUPPORTED +# endif # endif #endif diff --git a/include/nana/filesystem/filesystem.hpp b/include/nana/filesystem/filesystem.hpp index 067586f5..b67a00ef 100644 --- a/include/nana/filesystem/filesystem.hpp +++ b/include/nana/filesystem/filesystem.hpp @@ -1,7 +1,7 @@ /** * A ISO C++ filesystem Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) + * 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 @@ -55,6 +55,36 @@ #define NANA_USING_BOOST_FILESYSTEM 1 # include # include +// dont include generic_u8string +// http://www.boost.org/doc/libs/1_66_0/boost/filesystem/path.hpp +// enable directory_iterator C++11 range-base for +// http://www.boost.org/doc/libs/1_66_0/boost/filesystem/operations.hpp +// but travis come with an oooold version of boost +// NOT enable directory_iterator C++11 range-base for +// http://www.boost.org/doc/libs/1_54_0/boost/filesystem/operations.hpp +namespace boost +{ + namespace filesystem + { + + // enable directory_iterator C++11 range-base for statement use --------------------// + + // begin() and end() are only used by a range-based for statement in the context of + // auto - thus the top-level const is stripped - so returning const is harmless and + // emphasizes begin() is just a pass through. + inline const directory_iterator& begin(const directory_iterator& iter) BOOST_NOEXCEPT + { + return iter; + } + + inline directory_iterator end(const directory_iterator&) BOOST_NOEXCEPT + { + return directory_iterator(); + } + + } // namespace filesystem +} + // add boost::filesystem into std::experimental::filesystem namespace std { @@ -75,6 +105,16 @@ namespace std { socket = boost::filesystem::file_type::socket_file, unknown = boost::filesystem::file_type::type_unknown, }; + /// enable directory_iterator range-based for statements + //inline directory_iterator begin(directory_iterator iter) noexcept + //{ + // return iter; + //} + + //inline directory_iterator end(const directory_iterator&) noexcept + //{ + // return {}; + //} } // filesystem } // experimental } // std diff --git a/include/nana/filesystem/filesystem_ext.hpp b/include/nana/filesystem/filesystem_ext.hpp index 2c28acad..959b8e33 100644 --- a/include/nana/filesystem/filesystem_ext.hpp +++ b/include/nana/filesystem/filesystem_ext.hpp @@ -1,6 +1,6 @@ /** * Nana C++ Library(http://www.nanapro.org) -* Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) +* 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 @@ -16,6 +16,7 @@ #define NANA_FILESYSTEM_EXT_HPP #include +#include namespace nana { @@ -34,6 +35,16 @@ namespace filesystem_ext std::experimental::filesystem::path path_user(); ///< extention ? +/// workaround Boost not having path.generic_u8string() - a good point for http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0251r0.pdf +inline std::string generic_u8string(const std::experimental::filesystem::path& p) +{ + #if NANA_USING_BOOST_FILESYSTEM + return nana::to_utf8(p.generic_wstring()); + #else + return p.generic_u8string(); + #endif +} + inline bool is_directory(const std::experimental::filesystem::directory_entry& dir) noexcept { return is_directory(dir.status()); diff --git a/include/nana/gui/basis.hpp b/include/nana/gui/basis.hpp index 2cc656ca..4153d439 100644 --- a/include/nana/gui/basis.hpp +++ b/include/nana/gui/basis.hpp @@ -4,7 +4,7 @@ * * Basis Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) + * 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 @@ -30,6 +30,15 @@ namespace nana struct native_drawable_impl{}; } + struct accel_key + { + char key; + bool case_sensitive{ false }; + bool alt{ false }; + bool ctrl{ false }; + bool shift{ false }; + }; + enum class checkstate { unchecked, checked, partial @@ -288,6 +297,19 @@ that return a corresponding nana::appearance with predefined values. virtual bool visible() const = 0; };//end class caret_interface + /// Interface for scroll operations + /** + * This interface provides methods to operate the scrollbars that are contained + * in a specific widget, such as listbox and treebox + */ + class scroll_operation_interface + { + public: + virtual ~scroll_operation_interface() = default; + + virtual bool visible(bool vert) const = 0; + }; + namespace parameters { /// The system-wide parameters for mouse wheel diff --git a/include/nana/gui/detail/bedrock.hpp b/include/nana/gui/detail/bedrock.hpp index 0db691b3..f0d3f63c 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-2017 Jinhao(cnjinhao@hotmail.com) + * 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 @@ -27,6 +27,7 @@ namespace detail struct basic_window; class window_manager; + struct window_platform_assoc; /// @brief fundamental core component, it provides an abstraction to the OS platform and some basic functions. class bedrock @@ -73,6 +74,11 @@ namespace detail //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(unsigned thread_id); + + public: + //Platform-dependent functions + 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); diff --git a/include/nana/gui/detail/effects_renderer.hpp b/include/nana/gui/detail/effects_renderer.hpp index 3727933a..3a980caa 100644 --- a/include/nana/gui/detail/effects_renderer.hpp +++ b/include/nana/gui/detail/effects_renderer.hpp @@ -1,3 +1,15 @@ +/* +* 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 @@ -76,7 +88,7 @@ namespace nana{ nana::rectangle r; for(auto & action : nimbus) { - if(_m_edge_nimbus(focused, action.window) && window_layer::read_visual_rectangle(action.window, r)) + if(_m_edge_nimbus(action.window, focused) && window_layer::read_visual_rectangle(action.window, r)) { if (action.window == wd) { @@ -140,12 +152,17 @@ namespace nana{ } } private: - static bool _m_edge_nimbus(core_window_t * focused_wd, core_window_t * wd) + /// 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) { - 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; + // 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; } diff --git a/include/nana/gui/detail/general_events.hpp b/include/nana/gui/detail/general_events.hpp index 5992f1a0..72b4cbf3 100644 --- a/include/nana/gui/detail/general_events.hpp +++ b/include/nana/gui/detail/general_events.hpp @@ -1,7 +1,7 @@ /** * Definition of General Events * Nana C++ Library(http://www.nanapro.org) -* Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) +* 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 @@ -460,8 +460,9 @@ namespace nana ::nana::window window_handle; ///< A handle to the event window mutable wchar_t key; ///< the key corresponding to the key pressed mutable bool ignore; ///< this member is only available for key_char event, set 'true' to ignore the input. - bool ctrl; ///< keyboard Ctrl is pressed? - bool shift; ///< keyboard Shift is pressed + bool alt; ///< it is set to indicate the modifier key Alt just prior to the event. + bool ctrl; ///< it is set to indicate the modifier key Ctrl just prior to the event. + bool shift; ///< it is set to indicate the modifier key Shift just prior to the event. }; struct arg_move : public event_arg diff --git a/include/nana/gui/programming_interface.hpp b/include/nana/gui/programming_interface.hpp index 8c0d85fe..cb795e5f 100644 --- a/include/nana/gui/programming_interface.hpp +++ b/include/nana/gui/programming_interface.hpp @@ -301,6 +301,8 @@ namespace API void window_size(window, const size&); size window_outline_size(window); void window_outline_size(window, const size&); + + nana::optional window_rectangle(window); bool get_window_rectangle(window, rectangle&); bool track_window_size(window, const size&, bool true_for_max); ///< Sets the minimum or maximum tracking size of a window. void window_enabled(window, bool); @@ -434,6 +436,8 @@ namespace API * widget by the content extent. */ optional> content_extent(window wd, unsigned limited_px, bool limit_width); + + unsigned screen_dpi(bool x_requested); }//end namespace API }//end namespace nana diff --git a/include/nana/gui/widgets/checkbox.hpp b/include/nana/gui/widgets/checkbox.hpp index 8d06b590..e3b31d69 100644 --- a/include/nana/gui/widgets/checkbox.hpp +++ b/include/nana/gui/widgets/checkbox.hpp @@ -1,7 +1,7 @@ /** * A CheckBox Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) + * 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 @@ -100,6 +100,7 @@ namespace drawerbase event_handle eh_keyboard; }; public: + constexpr static const std::size_t npos = static_cast(-1); ~radio_group(); void add(checkbox&); std::size_t checked() const; ///< Retrieves the index of the checkbox which is checked. diff --git a/include/nana/gui/widgets/form.hpp b/include/nana/gui/widgets/form.hpp index 77972a8b..6053a1d7 100644 --- a/include/nana/gui/widgets/form.hpp +++ b/include/nana/gui/widgets/form.hpp @@ -1,7 +1,7 @@ /** * A Form Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com) + * 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 @@ -68,6 +68,8 @@ namespace nana void modality() const; void wait_for_this(); + + void keyboard_accelerator(const accel_key&, const std::function& fn); }; class nested_form diff --git a/include/nana/gui/widgets/group.hpp b/include/nana/gui/widgets/group.hpp index 2e264289..6ec70c0a 100644 --- a/include/nana/gui/widgets/group.hpp +++ b/include/nana/gui/widgets/group.hpp @@ -1,7 +1,7 @@ /** * A group widget implementation * Nana C++ Library(http://www.nanaro.org) - * Copyright(C) 2015 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2015-2018 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,7 @@ namespace nana{ struct implement; public: using field_reference = place::field_reference; + constexpr static const std::size_t npos = static_cast(-1); /// The default construction group(); @@ -63,9 +64,13 @@ namespace nana{ group& enable_format_caption(bool format); - group& collocate() throw(); - group& div(const char* div_str) throw(); + group& collocate() noexcept; + group& div(const char* div_str) noexcept; field_reference operator[](const char* field); + + void field_display(const char* field_name, bool display); /// Widget* create_child(const char* field, Args && ... args) diff --git a/include/nana/gui/widgets/listbox.hpp b/include/nana/gui/widgets/listbox.hpp index 71280b7e..3f2caab6 100644 --- a/include/nana/gui/widgets/listbox.hpp +++ b/include/nana/gui/widgets/listbox.hpp @@ -1,7 +1,7 @@ /** * A List Box Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) + * 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 @@ -94,6 +94,12 @@ namespace nana */ virtual void fit_content(unsigned maximum = 0) noexcept = 0; + /// Sets an exclusive font for the column + virtual void typeface(const paint::font& column_font) = 0; + + /// Returns a font + virtual paint::font typeface() const noexcept = 0; + /// Determines the visibility state of the column /** * @return true if the column is visible, false otherwise @@ -987,9 +993,13 @@ namespace nana cat_proxy(essence*, size_type pos) noexcept; cat_proxy(essence*, category_t*) noexcept; - /// Append an item at abs end of the category, set_value determines whether assign T object to the value of item. - template - item_proxy append(T&& t, bool set_value = false) + /// Append an item at the end of this category using the oresolver to generate the texts to be put in each column. + /// + /// First you have to make sure there is an overload of the operator<<() of the oresolver for the type of the object used here + /// If a listbox have a model set, try call append_model instead. + template + item_proxy append( T&& t, ///< Value used by the resolver to generate the texts to be put in each column of the item + bool set_value = false) ///< determines whether to set the object as the value of this item. { oresolver ores(ess_); @@ -1039,7 +1049,7 @@ namespace nana model_guard model(); - /// Appends one item at the end of this category with the specifies text in the column fields + /// Appends one item at the end of this category with the specifies texts in the column fields void append(std::initializer_list texts_utf8); void append(std::initializer_list texts); @@ -1194,17 +1204,25 @@ namespace nana color_proxy selection_box{ static_cast(0x3399FF) }; ///< Color of selection box border. + + std::shared_ptr column_font; ///< Renderer draws column texts with the font if it is not a nullptr. + /// The max column width which is generated by fit_content is allowed. It is ignored when it is 0, or a max value is passed to fit_content. unsigned max_fit_content{ 0 }; - unsigned min_column_width{ 20 }; ///< def=20 . non counting suspension_width + 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 header_height { 25 }; ///< def=25 . header height header_size - unsigned item_height_ex { 6 }; ///< Set !=0 !!!! def=6. item_height = text_height + item_height_ex + 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 + + //deprecated + //unsigned header_height { 25 }; ///< def=25 . header height header_size + + unsigned item_height_ex{ 6 }; ///< Set !=0 !!!! def=6. item_height = text_height + item_height_ex unsigned header_splitter_area_before{ 2 }; ///< def=2. But 4 is better... IMO - unsigned header_splitter_area_after { 3 }; ///< def=3. But 4 is better... + unsigned header_splitter_area_after{ 3 }; ///< def=3. But 4 is better... + unsigned header_padding_top{ 3 }; + unsigned header_padding_bottom{ 3 }; ::nana::parameters::mouse_wheel mouse_wheel{}; ///< The number of lines/characters to scroll when vertical/horizontal mouse wheel is moved. }; @@ -1411,6 +1429,8 @@ the nana::detail::basic_window member pointer scheme size_type append_header(std::string text_utf8, unsigned width = 120); size_type append_header(std::wstring text, unsigned width = 120); + void clear_headers(); ///< Removes all the columns. + cat_proxy append(std::string category); ///< Appends a new category to the end cat_proxy append(std::wstring category); ///< Appends a new category to the end void append(std::initializer_list categories); ///< Appends categories to the end @@ -1521,6 +1541,7 @@ the nana::detail::basic_window member pointer scheme nana::any* _m_anyobj(size_type cat, size_type index, bool allocate_if_empty) const override; drawerbase::listbox::category_t* _m_assoc(std::shared_ptr, bool create_if_not_exists); void _m_erase_key(nana::detail::key_interface*) noexcept; + std::shared_ptr _m_scroll_operation(); }; }//end namespace nana diff --git a/include/nana/gui/widgets/menubar.hpp b/include/nana/gui/widgets/menubar.hpp index 24c965b5..75158284 100644 --- a/include/nana/gui/widgets/menubar.hpp +++ b/include/nana/gui/widgets/menubar.hpp @@ -95,6 +95,7 @@ namespace nana menu& push_back(const std::string&); ///< Appends a new (empty) menu. menu& at(size_t index) const; ///< Gets the menu specified by index. std::size_t length() const; ///< Number of menus. + void clear(); ///< Removes all the menus. /// Deselects the menu /** diff --git a/include/nana/gui/widgets/scroll.hpp b/include/nana/gui/widgets/scroll.hpp index c4e39981..81bc0a60 100644 --- a/include/nana/gui/widgets/scroll.hpp +++ b/include/nana/gui/widgets/scroll.hpp @@ -1,7 +1,7 @@ /** * A Scroll Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com) + * 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 @@ -147,7 +147,7 @@ namespace nana void step(size_type s) { - metrics_.step = s; + metrics_.step = (s ? s : 1); } bool make_step(bool forward, unsigned multiple) @@ -398,7 +398,7 @@ namespace nana /// \brief The construct that creates a widget. /// @param wd A handle to the parent window of the widget being created. /// @param visible specify the visibility after creation. - scroll(window wd, bool visible) + scroll(window wd, bool visible = true) { this->create(wd, rectangle(), visible); // add a widget scheme? and take some colors from these wd? } @@ -501,7 +501,8 @@ namespace nana /// @return true if the vlaue is changed. bool make_page_scroll(bool forward) { - return this->make_step(forward, static_cast(range() - 1)); + auto const count = range() / step(); + return this->make_step(forward, static_cast(count > 2 ? count - 1 : 1)); } };//end class scroll }//end namespace nana diff --git a/include/nana/gui/widgets/skeletons/text_editor.hpp b/include/nana/gui/widgets/skeletons/text_editor.hpp index a2ab3ea7..d3536182 100644 --- a/include/nana/gui/widgets/skeletons/text_editor.hpp +++ b/include/nana/gui/widgets/skeletons/text_editor.hpp @@ -1,7 +1,7 @@ /* * A text editor implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) + * 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 @@ -219,6 +219,8 @@ namespace nana{ namespace widgets const skeletons::textbase& textbase() const; bool try_refresh(); + + std::shared_ptr scroll_operation() const; private: nana::color _m_draw_colored_area(paint::graphics& graph, const std::pair& row, bool whole_line); std::vector _m_render_text(const ::nana::color& text_color); diff --git a/include/nana/gui/widgets/tabbar.hpp b/include/nana/gui/widgets/tabbar.hpp index 0e2e2e9c..a24cce4a 100644 --- a/include/nana/gui/widgets/tabbar.hpp +++ b/include/nana/gui/widgets/tabbar.hpp @@ -1,7 +1,7 @@ /** * A Tabbar implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) + * 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 @@ -42,9 +42,8 @@ namespace nana : arg_tabbar({wdg, v}) {} - bool remove = true; ///< determines whether to remove the item - bool close_attach_window = true; ///< determines whether to close the attached window. It is ignored if remove is false - + mutable bool remove = true; ///< determines whether to remove the item + mutable bool close_attach_window = true; ///< determines whether to close the attached window. It is ignored if remove is false }; namespace drawerbase @@ -293,7 +292,7 @@ namespace nana if (pos > length()) throw std::out_of_range("tabbar::insert invalid position"); - this->get_drawer_trigger().insert(pos, to_nstring(text), std::move(value)); + this->get_drawer_trigger().insert(pos, to_nstring(std::move(text)), std::move(value)); API::update_window(*this); } diff --git a/include/nana/gui/widgets/textbox.hpp b/include/nana/gui/widgets/textbox.hpp index 5f6dbc91..de70728c 100644 --- a/include/nana/gui/widgets/textbox.hpp +++ b/include/nana/gui/widgets/textbox.hpp @@ -1,7 +1,7 @@ /** * A Textbox Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) + * 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 @@ -279,6 +279,7 @@ namespace nana native_string_type _m_caption() const throw() override; void _m_caption(native_string_type&&) override; void _m_typeface(const paint::font&) override; + std::shared_ptr _m_scroll_operation() const; }; }//end namespace nana #include diff --git a/include/nana/gui/widgets/toolbar.hpp b/include/nana/gui/widgets/toolbar.hpp index d92c7f8a..02e88c6c 100644 --- a/include/nana/gui/widgets/toolbar.hpp +++ b/include/nana/gui/widgets/toolbar.hpp @@ -1,108 +1,118 @@ -/** - * A Toolbar Implementation - * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) - * - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at - * http://www.boost.org/LICENSE_1_0.txt) - * - * @file: nana/gui/widgets/toolbar.hpp - */ - -#ifndef NANA_GUI_WIDGET_TOOLBAR_HPP -#define NANA_GUI_WIDGET_TOOLBAR_HPP -#include - -#include "widget.hpp" - -namespace nana -{ - class toolbar; - - struct arg_toolbar - : public event_arg - { - toolbar& widget; - std::size_t button; - - arg_toolbar(toolbar&, std::size_t); - }; - - namespace drawerbase - { - namespace toolbar - { - struct toolbar_events - : public general_events - { - basic_event selected; ///< A mouse click on a control button. - basic_event enter; ///< The mouse enters a control button. - basic_event leave; ///< The mouse leaves a control button. - }; - - struct item_type; - class item_container; - - class drawer - : public drawer_trigger - { - struct drawer_impl_type; - - public: - using size_type = std::size_t; - - drawer(); - ~drawer(); - - item_container& items() const; - void scale(unsigned); - private: - void refresh(graph_reference) override; - void attached(widget_reference, graph_reference) override; - void detached() override; - void mouse_move(graph_reference, const arg_mouse&) override; - void mouse_leave(graph_reference, const arg_mouse&) override; - void mouse_down(graph_reference, const arg_mouse&) override; - void mouse_up(graph_reference, const arg_mouse&) override; - private: - size_type _m_which(point, bool want_if_disabled) const; - void _m_calc_pixels(item_type*, bool force); - private: - ::nana::toolbar* widget_; - drawer_impl_type* impl_; - }; - - }//end namespace toolbar - }//end namespace drawerbase - - /// Control bar that contains buttons for controlling - class toolbar - : public widget_object - { - public: - using size_type = std::size_t; ///< A type to count the number of elements. - - toolbar() = default; - toolbar(window, bool visible, bool detached=false); - toolbar(window, const rectangle& = rectangle(), bool visible = true, bool detached = false); - - void separate(); ///< Adds a separator. - void append(const ::std::string& text, const nana::paint::image& img); ///< Adds a control button. - void append(const ::std::string& text); ///< Adds a control button. - bool enable(size_type index) const; - void enable(size_type index, bool enable_state); - 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. - void go_right(); - - bool detached() { return detached_; }; - - private: - bool detached_; - }; -}//end namespace nana -#include - -#endif +/** + * A Toolbar Implementation + * 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/widgets/toolbar.hpp + */ + +#ifndef NANA_GUI_WIDGET_TOOLBAR_HPP +#define NANA_GUI_WIDGET_TOOLBAR_HPP +#include + +#include "widget.hpp" + +namespace nana +{ + class toolbar; + + struct arg_toolbar + : public event_arg + { + toolbar& widget; + std::size_t button; + + arg_toolbar(toolbar&, std::size_t); + }; + + namespace drawerbase + { + namespace toolbar + { + struct item_proxy + { + nana::toolbar& widget; + std::size_t button; + + void enable(bool enable_state); + }; + + struct toolbar_events + : public general_events + { + basic_event selected; ///< A mouse click on a control button. + basic_event enter; ///< The mouse enters a control button. + basic_event leave; ///< The mouse leaves a control button. + }; + + struct item_type; + class item_container; + + class drawer + : public drawer_trigger + { + struct drawer_impl_type; + + public: + using size_type = std::size_t; + + drawer(); + ~drawer(); + + item_container& items() const; + void scale(unsigned); + private: + void refresh(graph_reference) override; + void attached(widget_reference, graph_reference) override; + void detached() override; + void mouse_move(graph_reference, const arg_mouse&) override; + void mouse_leave(graph_reference, const arg_mouse&) override; + void mouse_down(graph_reference, const arg_mouse&) override; + void mouse_up(graph_reference, const arg_mouse&) override; + private: + size_type _m_which(point, bool want_if_disabled) const; + void _m_calc_pixels(item_type*, bool force); + private: + ::nana::toolbar* widget_; + drawer_impl_type* impl_; + }; + + }//end namespace toolbar + }//end namespace drawerbase + + /// Control bar that contains buttons for controlling + class toolbar + : public widget_object + { + public: + using size_type = std::size_t; ///< A type to count the number of elements. + + toolbar() = default; + toolbar(window, bool visible, bool detached=false); + toolbar(window, const rectangle& = rectangle(), bool visible = true, bool detached = false); + + void separate(); ///< Adds a separator. + drawerbase::toolbar::item_proxy append(const ::std::string& text, const nana::paint::image& img); ///< Adds a control button. + drawerbase::toolbar::item_proxy append(const ::std::string& text); ///< Adds a control button. + void clear(); ///< Removes all control buttons and separators. + + bool enable(size_type index) const; + void enable(size_type index, bool enable_state); + 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. + void go_right(); + + bool detached() { return detached_; }; + + private: + bool detached_; + }; +}//end namespace nana +#include + +#endif diff --git a/include/nana/gui/widgets/treebox.hpp b/include/nana/gui/widgets/treebox.hpp index 08548dd4..75be5da0 100644 --- a/include/nana/gui/widgets/treebox.hpp +++ b/include/nana/gui/widgets/treebox.hpp @@ -1,7 +1,7 @@ /** * A Tree Box Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE or copy at @@ -445,6 +445,9 @@ namespace nana item_proxy selected() const; ///< returns the selected node + private: + std::shared_ptr _m_scroll_operation() const; + };//end class treebox }//end namespace nana diff --git a/include/nana/gui/widgets/widget.hpp b/include/nana/gui/widgets/widget.hpp index 0ee37703..10a3e352 100644 --- a/include/nana/gui/widgets/widget.hpp +++ b/include/nana/gui/widgets/widget.hpp @@ -1,7 +1,7 @@ /** * The fundamental widget class implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) + * 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 @@ -83,6 +83,8 @@ namespace nana void focus(); bool focused() const; + std::shared_ptr scroll_operation(); + void show(); ///< Sets the window visible. void hide(); ///< Sets the window invisible. bool visible() const; @@ -137,6 +139,7 @@ namespace nana virtual void _m_close(); virtual bool _m_enabled() const; virtual void _m_enabled(bool); + virtual std::shared_ptr _m_scroll_operation(); virtual bool _m_show(bool); virtual bool _m_visible() const; virtual void _m_size(const nana::size&); diff --git a/include/nana/paint/graphics.hpp b/include/nana/paint/graphics.hpp index 9ce35704..ad0ef379 100644 --- a/include/nana/paint/graphics.hpp +++ b/include/nana/paint/graphics.hpp @@ -38,14 +38,21 @@ namespace nana font(const font&); font(const ::std::string& name, double size_pt, const font_style& fs = {}); - font(double size_pt, const path_type& truetype, const font_style& ft = {}); + font(double size_pt, const path_type& truetype, const font_style& fs = {}); ~font(); bool empty() const; void set_default() const; ::std::string name() const; - double size() const; + + /// Returns font size, in point. + /** + * @param fixed Indicates whether to return a fixed font size. If this parameter is false, the method may return zero for default system font size. If the parameter is true, the method returns a fixed size of default font size if the font size that assigned by constructor is zero. + * @return The font size, in point. + */ + double size(bool fixed = false) const; + bool bold() const; unsigned weight() const; bool italic() const; diff --git a/include/nana/stdc++.hpp b/include/nana/stdc++.hpp index fc2d6568..c21d8a94 100644 --- a/include/nana/stdc++.hpp +++ b/include/nana/stdc++.hpp @@ -1,7 +1,7 @@ /** * Standard Library for C++11/14/17 * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2017 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2017-2018 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at diff --git a/source/detail/platform_abstraction.cpp b/source/detail/platform_abstraction.cpp index 6490045c..2d05a35f 100644 --- a/source/detail/platform_abstraction.cpp +++ b/source/detail/platform_abstraction.cpp @@ -255,6 +255,35 @@ namespace nana data::storage = nullptr; } + double platform_abstraction::font_default_pt() + { +#ifdef NANA_WINDOWS + //Create default font object. + NONCLIENTMETRICS metrics = {}; + metrics.cbSize = sizeof metrics; +#if(WINVER >= 0x0600) +#if defined(NANA_MINGW) + OSVERSIONINFO osvi = {}; + osvi.dwOSVersionInfoSize = sizeof(osvi); + ::GetVersionEx(&osvi); + if (osvi.dwMajorVersion < 6) + metrics.cbSize -= sizeof(metrics.iPaddedBorderWidth); +#else + if (!IsWindowsVistaOrGreater()) + metrics.cbSize -= sizeof(metrics.iPaddedBorderWidth); +#endif +#endif + ::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof metrics, &metrics, 0); + + auto desktop = ::GetDC(nullptr); + auto pt = std::abs(metrics.lfMessageFont.lfHeight) * 72.0 / ::GetDeviceCaps(desktop, LOGPIXELSY); + ::ReleaseDC(nullptr, desktop); + return pt; +#else + return 10; +#endif + } + ::std::shared_ptr platform_abstraction::default_font(const ::std::shared_ptr& new_font) { auto & r = platform_storage(); @@ -337,7 +366,7 @@ namespace nana if(font_family.empty()) font_family = '*'; - std::string pat_str = font_family + '-' + std::to_string(size_pt ? size_pt : 10); + 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()); XftResult res; auto match_pat = ::XftFontMatch(disp, ::XDefaultScreen(disp), pat, &res); @@ -407,6 +436,31 @@ namespace nana ::FcConfigAppFontClear(nullptr); } } +#endif + } + + unsigned platform_abstraction::screen_dpi(bool x_requested) + { +#ifdef NANA_WINDOWS + auto hdc = ::GetDC(nullptr); + auto dots = static_cast(::GetDeviceCaps(hdc, (x_requested ? LOGPIXELSX : LOGPIXELSY))); + ::ReleaseDC(nullptr, hdc); + return dots; +#else + auto & spec = ::nana::detail::platform_spec::instance(); + auto disp = spec.open_display(); + auto screen = ::XDefaultScreen(disp); + + double dots; + + if (x_requested) + dots += ((((double)DisplayWidth(disp, screen)) * 25.4) / + ((double)DisplayWidthMM(disp, screen))); + else + dots += ((((double)DisplayHeight(disp, screen)) * 25.4) / + ((double)DisplayHeightMM(disp, screen))); + + return static_cast(dots + 0.5); #endif } } diff --git a/source/detail/platform_abstraction.hpp b/source/detail/platform_abstraction.hpp index d22190c6..290e0a5c 100644 --- a/source/detail/platform_abstraction.hpp +++ b/source/detail/platform_abstraction.hpp @@ -33,10 +33,13 @@ namespace nana static void initialize(); /// Shutdown before destruction of platform_spec static void shutdown(); + static double font_default_pt(); 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); static void font_resource(bool try_add, const path_type& ttf); + + static unsigned screen_dpi(bool x_requested); }; } diff --git a/source/gui/detail/bedrock_posix.cpp b/source/gui/detail/bedrock_posix.cpp index 5f4fa1fa..b8edb34c 100644 --- a/source/gui/detail/bedrock_posix.cpp +++ b/source/gui/detail/bedrock_posix.cpp @@ -1,7 +1,7 @@ /* * A Bedrock Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) + * 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 @@ -15,10 +15,10 @@ #include #include #include -#include #include #include #include +#include "inner_fwd_implement.hpp" #include #include @@ -104,6 +104,42 @@ namespace detail void window_proc_for_packet(Display *, nana::detail::msg_packet_tag&); void window_proc_for_xevent(Display*, XEvent&); + class accel_key_comparer + { + public: + bool operator()(const accel_key& a, const accel_key& b) const + { + auto va = a.case_sensitive ? a.key : std::tolower(a.key); + auto vb = b.case_sensitive ? b.key : std::tolower(b.key); + + if(va < vb) + return true; + else if(va > vb) + return false; + + if (a.case_sensitive != b.case_sensitive) + return b.case_sensitive; + + if (a.alt != b.alt) + return b.alt; + + if (a.ctrl != b.ctrl) + return b.ctrl; + + return ((a.shift != b.shift) && b.shift); + } + }; + + struct accel_key_value + { + std::function command; + }; + + struct window_platform_assoc + { + std::map accel_commands; + }; + //class bedrock defines a static object itself to implement a static singleton //here is the definition of this object bedrock bedrock::bedrock_object; @@ -218,10 +254,28 @@ namespace detail { XKeyEvent xkey; nana::detail::platform_spec::instance().read_keystate(xkey); + arg.alt = (xkey.state & Mod1Mask); arg.ctrl = (xkey.state & ControlMask); arg.shift = (xkey.state & ShiftMask); } + void bedrock::delete_platform_assoc(window_platform_assoc* passoc) + { + delete passoc; + } + + void bedrock::keyboard_accelerator(native_window_type wd, const accel_key& ackey, const std::function& fn) + { + auto misc = wd_manager().root_runtime(wd); + if (nullptr == misc) + return; + + if (!misc->wpassoc) + misc->wpassoc = new window_platform_assoc; + + misc->wpassoc->accel_commands[ackey].command = fn; + } + element_store& bedrock::get_element_store() const { return impl_->estore; @@ -490,6 +544,34 @@ namespace detail return wchar_t(keysym); } + bool translate_keyboard_accelerator(root_misc* misc, char os_code, const arg_keyboard& modifiers) + { + if(!misc->wpassoc) + return false; + + auto lower_oc = std::tolower(os_code); + + std::function command; + + for(auto & accel : misc->wpassoc->accel_commands) + { + if(accel.first.key != (accel.first.case_sensitive ? os_code : lower_oc)) + continue; + + if(accel.first.alt == modifiers.alt && accel.first.ctrl == modifiers.ctrl && accel.first.shift == modifiers.shift) + { + command = accel.second.command; + break; + } + } + + if(!command) + return false; + + command(); + return true; + } + void window_proc_for_xevent(Display* /*display*/, XEvent& xevent) { typedef detail::bedrock::core_window_t core_window_t; @@ -849,6 +931,9 @@ namespace detail if(msgwnd) { + arg_keyboard modifiers_status; + brock.get_key_state(modifiers_status); + KeySym keysym; Status status; char fixbuf[33]; @@ -883,16 +968,20 @@ namespace detail keybuf[len] = 0; wchar_t os_code = 0; + bool accel_translated = false; + switch(status) { case XLookupKeySym: case XLookupBoth: os_code = os_code_from_keysym(keysym); + accel_translated = translate_keyboard_accelerator(root_runtime, os_code, modifiers_status); + if(accel_translated) + break; + if(os_code == keyboard::tab && (false == (msgwnd->flags.tab & detail::tab_type::eating))) //Tab { - arg_keyboard argkey; - brock.get_key_state(argkey); - auto tstop_wd = wd_manager.tabstop(msgwnd, !argkey.shift); + auto tstop_wd = wd_manager.tabstop(msgwnd, !modifiers_status.shift); if (tstop_wd) { root_runtime->condition.ignore_tab = true; @@ -907,9 +996,9 @@ namespace detail if((nullptr == pressed_wd) && (nullptr == pressed_wd_space)) { arg_mouse arg; - arg.alt = false; + arg.alt = modifiers_status.alt; arg.button = ::nana::mouse::left_button; - arg.ctrl = false; + arg.ctrl = modifiers_status.ctrl; arg.evt_code = event_code::mouse_down; arg.left_button = true; arg.mid_button = false; @@ -955,11 +1044,10 @@ namespace detail if(keyboard::os_ctrl == os_code) context.is_ctrl_pressed = true; - arg_keyboard arg; + arg_keyboard arg = modifiers_status; arg.ignore = false; arg.key = os_code; arg.evt_code = event_code::key_press; - brock.get_key_state(arg); arg.window_handle = reinterpret_cast(msgwnd); brock.emit(event_code::key_press, msgwnd, arg, true, &context); @@ -988,7 +1076,7 @@ namespace detail for(int i = 0; i < len; ++i) { - arg_keyboard arg; + arg_keyboard arg = modifiers_status; arg.ignore = false; arg.key = charbuf[i]; @@ -1011,7 +1099,6 @@ namespace detail } arg.evt_code = event_code::key_char; arg.window_handle = reinterpret_cast(msgwnd); - brock.get_key_state(arg); msgwnd->annex.events_ptr->key_char.emit(arg, reinterpret_cast(msgwnd)); if(arg.ignore == false && wd_manager.available(msgwnd)) draw_invoker(&drawer::key_char, msgwnd, arg, &context); diff --git a/source/gui/detail/bedrock_windows.cpp b/source/gui/detail/bedrock_windows.cpp index 2d75e7d4..ac1626ef 100644 --- a/source/gui/detail/bedrock_windows.cpp +++ b/source/gui/detail/bedrock_windows.cpp @@ -1,7 +1,7 @@ /** * A Bedrock Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) + * 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 @@ -20,11 +20,11 @@ #include #include #include -#include #include #include #include #include +#include "inner_fwd_implement.hpp" #include //use std::cerr @@ -182,6 +182,12 @@ namespace detail }cache; }; + struct window_platform_assoc + { + HACCEL accel{ nullptr }; ///< A handle to a Windows keyboard accelerator object. + std::map> accel_commands; + }; + //class bedrock defines a static object itself to implement a static singleton //here is the definition of this object bedrock bedrock::bedrock_object; @@ -345,6 +351,25 @@ namespace detail } } + void process_msg(bedrock* brock, MSG& msg) + { + if (WM_KEYFIRST <= msg.message && msg.message <= WM_KEYLAST) + { + auto misc = brock->wd_manager().root_runtime(reinterpret_cast(msg.hwnd)); + if (misc && misc->wpassoc && misc->wpassoc->accel) + { + if (::TranslateAccelerator(msg.hwnd, misc->wpassoc->accel, &msg)) + return; + } + } + + auto menu_wd = brock->get_menu(reinterpret_cast(msg.hwnd), true); + if (menu_wd) interior_helper_for_menu(msg, menu_wd); + + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + void bedrock::pump_event(window condition_wd, bool is_modal) { const unsigned tid = ::GetCurrentThreadId(); @@ -383,11 +408,15 @@ namespace detail if (msg.message == WM_QUIT) break; if ((WM_KEYFIRST <= msg.message && msg.message <= WM_KEYLAST) || !::IsDialogMessage(native_handle, &msg)) { +#if 0 auto menu_wd = get_menu(reinterpret_cast(msg.hwnd), true); if (menu_wd) interior_helper_for_menu(msg, menu_wd); - ::TranslateMessage(&msg); + ::TranslateMessage(&msg); //deprecated ::DispatchMessage(&msg); +#else + process_msg(this, msg); +#endif wd_manager().remove_trash_handle(tid); } @@ -400,11 +429,15 @@ namespace detail { if (-1 != ::GetMessage(&msg, 0, 0, 0)) { +#if 0 auto menu_wd = get_menu(reinterpret_cast(msg.hwnd), true); if (menu_wd) interior_helper_for_menu(msg, menu_wd); ::TranslateMessage(&msg); ::DispatchMessage(&msg); +#else + process_msg(this, msg); +#endif } wd_manager().call_safe_place(tid); @@ -420,11 +453,15 @@ namespace detail { if(-1 != ::GetMessage(&msg, 0, 0, 0)) { +#if 0 auto menu_wd = get_menu(reinterpret_cast(msg.hwnd), true); if(menu_wd) interior_helper_for_menu(msg, menu_wd); ::TranslateMessage(&msg); ::DispatchMessage(&msg); +#else + process_msg(this, msg); +#endif } wd_manager().call_safe_place(tid); @@ -635,6 +672,7 @@ namespace detail switch(msg) { + case WM_COMMAND: case WM_DESTROY: case WM_SHOWWINDOW: case WM_SIZING: @@ -786,6 +824,17 @@ namespace detail switch (message) { + case WM_COMMAND: + if ((1 == HIWORD(wParam)) && root_runtime->wpassoc) + { + auto i = root_runtime->wpassoc->accel_commands.find(LOWORD(wParam)); + if (i != root_runtime->wpassoc->accel_commands.end()) + { + auto fn = i->second; + fn(); + } + } + break; case WM_IME_STARTCOMPOSITION: if (msgwnd->other.attribute.root->ime_enabled) { @@ -1578,10 +1627,58 @@ namespace detail void bedrock::get_key_state(arg_keyboard& kb) { + kb.alt = (0 != (::GetKeyState(VK_MENU) & 0x80)); kb.ctrl = (0 != (::GetKeyState(VK_CONTROL) & 0x80)); kb.shift = (0 != (::GetKeyState(VK_SHIFT) & 0x80)); } + void bedrock::delete_platform_assoc(window_platform_assoc* passoc) + { + delete passoc; + } + + //Generates an identitifer for an accel key. + std::pair id_accel_key(const accel_key& key) + { + std::pair ret; + + //Use virt-key for non-case sensitive + if (!key.case_sensitive) + ret.second = static_cast(std::tolower(key.key) - 'a' + 0x41); + + ret.first = ret.second | int(key.case_sensitive ? (1 << 8) : 0) | int(key.alt ? (1 << 9) : 0) | int(key.ctrl ? (1 << 10) : 0) | int(key.shift ? (1 << 11) : 0); + return ret; + } + + void bedrock::keyboard_accelerator(native_window_type wd, const accel_key& key, const std::function& fn) + { + auto misc = wd_manager().root_runtime(wd); + if (nullptr == misc) + return; + + if (!misc->wpassoc) + misc->wpassoc = new window_platform_assoc; + + auto idkey = id_accel_key(key); + + misc->wpassoc->accel_commands[idkey.first] = fn; + + auto accel_size = ::CopyAcceleratorTable(misc->wpassoc->accel, nullptr, 0); + + std::unique_ptr accels(new ACCEL[accel_size + 1]); + + if (accel_size) + ::CopyAcceleratorTable(misc->wpassoc->accel, accels.get(), accel_size); + + auto p = accels.get() + accel_size; + p->cmd = idkey.first; + p->fVirt = (key.case_sensitive ? 0 : FVIRTKEY) | (key.alt ? FALT : 0) | (key.ctrl ? FCONTROL : 0) | (key.shift ? FSHIFT : 0); + p->key = idkey.second; + + ::DestroyAcceleratorTable(misc->wpassoc->accel); + misc->wpassoc->accel = ::CreateAcceleratorTable(accels.get(), accel_size + 1); + } + element_store& bedrock::get_element_store() const { return impl_->estore; diff --git a/include/nana/gui/detail/inner_fwd_implement.hpp b/source/gui/detail/inner_fwd_implement.hpp similarity index 86% rename from include/nana/gui/detail/inner_fwd_implement.hpp rename to source/gui/detail/inner_fwd_implement.hpp index 68937148..ed8ef73d 100644 --- a/include/nana/gui/detail/inner_fwd_implement.hpp +++ b/source/gui/detail/inner_fwd_implement.hpp @@ -1,7 +1,7 @@ /* * Implementations of Inner Forward Declaration * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) + * 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 @@ -15,9 +15,9 @@ #define NANA_GUI_INNER_FWD_IMPLEMENT_HPP #include -#include "inner_fwd.hpp" -#include "basic_window.hpp" -#include "../../paint/graphics.hpp" +#include +#include +#include #include @@ -54,10 +54,13 @@ namespace nana{ implementation * impl_; }; + struct window_platform_assoc; struct root_misc { basic_window * window; + window_platform_assoc * wpassoc{ nullptr }; + nana::paint::graphics root_graph; shortkey_container shortkeys; @@ -71,6 +74,10 @@ namespace nana{ root_misc(root_misc&&); root_misc(basic_window * wd, unsigned width, unsigned height); + ~root_misc(); + private: + root_misc(const root_misc&) = delete; + root_misc& operator=(const root_misc&) = delete; };//end struct root_misc diff --git a/source/gui/detail/native_window_interface.cpp b/source/gui/detail/native_window_interface.cpp index a054dba7..aee3ea8e 100644 --- a/source/gui/detail/native_window_interface.cpp +++ b/source/gui/detail/native_window_interface.cpp @@ -549,7 +549,7 @@ namespace nana{ { nana::detail::platform_spec::instance().keep_window_icon(wd, sml_icon, big_icon); if (sml_handle) - ::SendMessage(reinterpret_cast(wd), WM_SETICON, ICON_SMALL, reinterpret_cast(sml_handle)); + ::SendMessage(reinterpret_cast(wd), WM_SETICON, ICON_SMALL, reinterpret_cast(sml_handle)); if (big_handle) ::SendMessage(reinterpret_cast(wd), WM_SETICON, ICON_BIG, reinterpret_cast(big_handle)); diff --git a/source/gui/detail/window_layout.cpp b/source/gui/detail/window_layout.cpp index 27357217..45764b81 100644 --- a/source/gui/detail/window_layout.cpp +++ b/source/gui/detail/window_layout.cpp @@ -1,7 +1,7 @@ /* * Window Layout Implementation * Nana C++ Library(http://www.nanapro.org) -* Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) +* 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 @@ -106,43 +106,46 @@ namespace nana visual = rectangle{ wd->pos_root, wd->dimension }; - if (wd->root_widget != wd) + if (category::flags::root != wd->other.category) { //Test if the root widget is overlapped the specified widget //the pos of root widget is (0, 0) if (overlapped(visual, rectangle{ wd->root_widget->pos_owner, wd->root_widget->dimension }) == false) return false; - } - for (auto parent = wd->parent; parent; parent = parent->parent) - { - if (category::flags::root == parent->other.category) + for (auto parent = wd->parent; parent; parent = parent->parent) { - //visual rectangle of wd's parent - rectangle vrt_parent{parent->pos_root, parent->dimension}; - - point pos_root; - while (parent->parent) + if (category::flags::root == parent->other.category) { - pos_root -= native_interface::window_position(parent->root); - - if (!overlap(rectangle{ pos_root, parent->parent->root_widget->dimension }, vrt_parent, vrt_parent)) - return false; - - parent = parent->parent->root_widget; + wd = parent; + break; } - if (!overlap(vrt_parent, visual, visual)) + if (!overlap(rectangle{ parent->pos_root, parent->dimension }, visual, visual)) return false; - - return true; } - - if (!overlap(rectangle{ parent->pos_root, parent->dimension }, visual, visual)) - return false; } - return true; + //Now, wd actually is the root widget of original parameter wd + if (nullptr == wd->parent) + return true; + + auto parent_rw = wd->parent->root_widget; + //visual rectangle of wd's parent + rectangle vrt_parent{ parent_rw->pos_root, parent_rw->dimension }; + + point pos_root; + while (parent_rw->parent) + { + pos_root -= native_interface::window_position(parent_rw->root); + + if (!overlap(rectangle{ pos_root, parent_rw->parent->root_widget->dimension }, vrt_parent, vrt_parent)) + return false; + + parent_rw = parent_rw->parent->root_widget; + } + + return overlap(vrt_parent, visual, visual); } //read_overlaps @@ -386,6 +389,13 @@ namespace nana nana::rectangle r_of_sigwd(sigwd->pos_root, sigwd->dimension); for (auto wd : data_sect.effects_bground_windows) { + //Don't notify the window if both native root windows are not same(e.g. wd and sigwd have + //a some parent). Otherwise, _m_paint_glass_window() recursively paints sigwd to make stack overflow. + //On the other hand, a nested root window is always floating on its parent's child widgets, it's unnecessary to + //notify the wd if they haven't a same native root window. + if (sigwd->root != wd->root) + continue; + if (wd == sigwd || !wd->displayed() || (false == overlapped(nana::rectangle{ wd->pos_root, wd->dimension }, r_of_sigwd))) continue; diff --git a/source/gui/detail/window_manager.cpp b/source/gui/detail/window_manager.cpp index 4bfd1900..38add16a 100644 --- a/source/gui/detail/window_manager.cpp +++ b/source/gui/detail/window_manager.cpp @@ -1,7 +1,7 @@ /* * Window Manager Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) + * 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 @@ -16,11 +16,11 @@ #include #include #include -#include "window_register.hpp" #include -#include #include #include +#include "window_register.hpp" +#include "inner_fwd_implement.hpp" #include #include @@ -140,10 +140,12 @@ namespace nana //struct root_misc root_misc::root_misc(root_misc&& other): window(other.window), + wpassoc(other.wpassoc), root_graph(std::move(other.root_graph)), shortkeys(std::move(other.shortkeys)), condition(std::move(other.condition)) { + other.wpassoc = nullptr; //moved-from } root_misc::root_misc(basic_window * wd, unsigned width, unsigned height) @@ -155,6 +157,11 @@ namespace nana condition.pressed_by_space = nullptr; condition.hovered = nullptr; } + + root_misc::~root_misc() + { + bedrock::delete_platform_assoc(wpassoc); + } //end struct root_misc //class root_register diff --git a/source/gui/filebox.cpp b/source/gui/filebox.cpp index b31653cc..ff58039d 100644 --- a/source/gui/filebox.cpp +++ b/source/gui/filebox.cpp @@ -1041,7 +1041,8 @@ namespace nana bool filebox::show() const { #if defined(NANA_WINDOWS) - std::wstring wfile; + auto winitfile = to_wstring(impl_->file); + std::wstring wfile(winitfile); wfile.resize(520); OPENFILENAME ofn; diff --git a/source/gui/place.cpp b/source/gui/place.cpp index f243fcf0..d4ecf449 100644 --- a/source/gui/place.cpp +++ b/source/gui/place.cpp @@ -1,7 +1,7 @@ /* * An Implementation of Place for Layout * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) + * 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 @@ -58,7 +58,7 @@ namespace nana { div_start, div_end, splitter, identifier, dock, fit, hfit, vfit, vert, grid, number, array, reparray, - weight, width, height, 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, switchable, collapse, parameters, equal, eof, error @@ -252,14 +252,6 @@ namespace nana case 'd': return token::width; } } - else if ("dock" == idstr_) - return token::dock; - else if ("fit" == idstr_) - return token::fit; - else if ("vertical" == idstr_ || "vert" == idstr_) - return token::vert; - else if ("variable" == idstr_ || "repeated" == idstr_) - return ('v' == idstr_[0] ? token::variable : token::repeated); else if ("arrange" == idstr_ || "hfit" == idstr_ || "vfit" == idstr_ || "gap" == idstr_) { auto ch = idstr_[0]; @@ -287,16 +279,54 @@ namespace nana _m_throw_error("a parameter list is required after 'collapse'"); return token::collapse; } - else if ("left" == idstr_ || "right" == idstr_ || "top" == idstr_ || "bottom" == idstr_ || "undisplayed" == idstr_ || "invisible" == idstr_) + else if (!idstr_.empty()) { switch (idstr_.front()) { - case 'l': return token::left; - case 'r': return token::right; - case 't': return token::top; - case 'b': return token::bottom; - case 'u': return token::undisplayed; - case 'i': return token::invisible; + case 'b': + if ("bottom" == idstr_) + return token::bottom; + break; + case 'd': + if ("dock" == idstr_) + return token::dock; + break; + case 'f': + if ("fit" == idstr_) + return token::fit; + break; + case 'i': + if ("invisible" == idstr_) + return token::invisible; + break; + case 'l': + if ("left" == idstr_) + return token::left; + break; + case 'r': + if ("repeated" == idstr_) + return token::repeated; + else if ("right" == idstr_) + return token::right; + break; + case 's': + if ("switchable" == idstr_) + return token::switchable; + break; + case 't': + if ("top" == idstr_) + return token::top; + break; + case 'u': + if ("undisplayed" == idstr_) + return token::undisplayed; + break; + case 'v': + if ("vertical" == idstr_ || "vert" == idstr_) + return token::vert; + else if ("variable" == idstr_) + return token::variable; + break; } } @@ -579,6 +609,7 @@ namespace nana class div_splitter; class div_dock; class div_dockpane; + class div_switchable; window window_handle{nullptr}; event_handle event_size_handle{nullptr}; @@ -747,7 +778,7 @@ namespace nana class place::implement::division { public: - enum class kind{ arrange, vertical_arrange, grid, splitter, dock, dockpane}; + enum class kind{ arrange, vertical_arrange, grid, splitter, dock, dockpane, switchable}; using token = place_parts::tokenizer::token; division(kind k, std::string&& n) noexcept @@ -981,6 +1012,19 @@ namespace nana div_next->set_display(true); } } + + if (display) + { + //If the field is a child of switchable field, hides other child fields. + if (this->div_owner && (kind::switchable == this->div_owner->kind_of_division)) + { + for (auto & child : this->div_owner->children) + { + if (child.get() != this) + child->set_display(false); + } + } + } } bool is_back(const division* div) const noexcept @@ -2539,6 +2583,43 @@ namespace nana implement * const impl_; }; + class place::implement::div_switchable + : public division + { + public: + div_switchable(std::string && name, implement* impl) noexcept + : division(kind::switchable, std::move(name)), impl_(impl) + {} + private: + void collocate(window wd) override + { + division * div = nullptr; + for (auto & child : children) + { + if (child->display) + { + div = child.get(); + div->field_area = this->margin_area(); + div->collocate(wd); + break; + } + } + + //Hide other child fields. + rectangle empty_r{ this->margin_area().position() , size{ 0, 0 } }; + for (auto & child : children) + { + if (child.get() != div) + { + child->field_area = empty_r; + child->collocate(wd); + } + } + } + private: + implement * const impl_; + }; + place::implement::~implement() { API::umake_event(event_size_handle); @@ -2680,6 +2761,9 @@ namespace nana children.emplace_back(std::move(div)); } break; + case token::switchable: + div_type = token::switchable; + break; case token::vert: div_type = tk; break; @@ -2850,6 +2934,9 @@ namespace nana 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."); } @@ -3184,6 +3271,22 @@ namespace nana modified_ptr->div_owner = div_owner; modified_ptr->div_next = div_next; + + if (div_owner) + { + implement::division * pv_div = nullptr; + //Updates the div_next of the div at front of modified one. + for (auto & div : div_owner->children) + { + if (div.get() == modified_ptr) + { + if (pv_div) + pv_div->div_next = modified_ptr; + break; + } + pv_div = div.get(); + } + } } catch (...) { diff --git a/source/gui/programming_interface.cpp b/source/gui/programming_interface.cpp index 22e22cac..7ed9883f 100644 --- a/source/gui/programming_interface.cpp +++ b/source/gui/programming_interface.cpp @@ -1,7 +1,7 @@ /* * Nana GUI Programming Interface Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) + * 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 @@ -20,6 +20,8 @@ #include #include +#include "../../source/detail/platform_abstraction.hpp" + namespace nana { //restrict @@ -821,6 +823,15 @@ namespace API } } + nana::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); + return{}; + } + bool get_window_rectangle(window wd, rectangle& r) { auto iwd = reinterpret_cast(wd); @@ -1473,5 +1484,10 @@ namespace API return{}; } + + unsigned screen_dpi(bool x_requested) + { + return ::nana::platform_abstraction::screen_dpi(x_requested); + } }//end namespace API }//end namespace nana diff --git a/source/gui/widgets/button.cpp b/source/gui/widgets/button.cpp index a01d001b..fe6f19c7 100644 --- a/source/gui/widgets/button.cpp +++ b/source/gui/widgets/button.cpp @@ -379,7 +379,7 @@ namespace nana{ namespace drawerbase }//end namespace drawerbase //button - //@brief: Defaine a button widget and it provides the interfaces to be operational + //@brief: Define a button widget and it provides the interfaces to be operational button::button(){} button::button(window wd, bool visible) diff --git a/source/gui/widgets/checkbox.cpp b/source/gui/widgets/checkbox.cpp index a1b2fe5c..7fc20469 100644 --- a/source/gui/widgets/checkbox.cpp +++ b/source/gui/widgets/checkbox.cpp @@ -1,7 +1,7 @@ /* * A CheckBox Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) + * 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 @@ -296,7 +296,7 @@ namespace nana{ namespace drawerbase return static_cast(i - ui_container_.cbegin()); } - return ui_container_.size(); + return npos; } std::size_t radio_group::size() const diff --git a/source/gui/widgets/form.cpp b/source/gui/widgets/form.cpp index 7f8247ff..9414a07b 100644 --- a/source/gui/widgets/form.cpp +++ b/source/gui/widgets/form.cpp @@ -1,6 +1,6 @@ /* * A Form Implementation - * Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com) + * 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 @@ -10,6 +10,7 @@ */ #include +#include namespace nana { @@ -94,6 +95,11 @@ namespace nana { API::wait_for(handle()); } + + void form::keyboard_accelerator(const accel_key& key, const std::function& fn) + { + nana::detail::bedrock::instance().keyboard_accelerator(this->native_handle(), key, fn); + } //end class form //class nested_form diff --git a/source/gui/widgets/group.cpp b/source/gui/widgets/group.cpp index ab8e9e73..b3c2658d 100644 --- a/source/gui/widgets/group.cpp +++ b/source/gui/widgets/group.cpp @@ -1,7 +1,7 @@ /** * A group widget implementation * Nana C++ Library(http://www.nanaro.org) - * Copyright(C) 2015-2017 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2015-2018 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -172,13 +172,13 @@ namespace nana{ return *this; } - group& group::collocate() throw () + group& group::collocate() noexcept { impl_->place_content.collocate(); return *this; } - group& group::div(const char* div_str) throw () + group& group::div(const char* div_str) noexcept { if (div_str) impl_->usr_div_str = div_str; @@ -194,6 +194,21 @@ namespace nana{ return impl_->place_content.field(field); } + void group::field_display(const char* field_name, bool display) + { + impl_->place_content.field_display(field_name, display); + } + + bool group::field_display(const char* field_name) const + { + return impl_->place_content.field_display(field_name); + } + + void group::erase(window handle) + { + impl_->place_content.erase(handle); + } + void group::_m_add_child(const char* field, widget* wdg) { impl_->place_content[field] << wdg->handle(); @@ -208,8 +223,8 @@ namespace nana{ outter[field_title] << impl_->caption; outter.collocate(); + impl_->caption.transparent(true); color pbg = API::bgcolor(this->parent()); - impl_->caption.bgcolor(pbg.blend(colors::black, 0.025)); this->bgcolor(pbg.blend(colors::black, 0.05)); @@ -222,10 +237,27 @@ namespace nana{ auto gap_px = impl_->gap - 1; graph.rectangle(true, API::bgcolor(this->parent())); - graph.round_rectangle(rectangle(point(gap_px, impl_->caption_dimension.height / 2), - nana::size(graph.width() - 2 * gap_px, graph.height() - impl_->caption_dimension.height / 2 - gap_px) + + auto const top_round_line = static_cast(impl_->caption_dimension.height) / 2; + + graph.round_rectangle(rectangle(point(gap_px, top_round_line), + nana::size(graph.width() - 2 * gap_px, graph.height() - top_round_line - gap_px) ), 3, 3, colors::gray_border, true, this->bgcolor()); + + auto opt_r = API::window_rectangle(impl_->caption); + if (opt_r) + { + rectangle grad_r{ opt_r->position(), nana::size{ opt_r->width, static_cast(top_round_line - opt_r->y) } }; + + grad_r.y += top_round_line*2 / 3; + grad_r.x -= 2; + grad_r.width += 4; + + graph.gradual_rectangle(grad_r, + API::bgcolor(this->parent()), this->bgcolor(), true + ); + } }); } diff --git a/source/gui/widgets/listbox.cpp b/source/gui/widgets/listbox.cpp index e473a080..7bdfc2b6 100644 --- a/source/gui/widgets/listbox.cpp +++ b/source/gui/widgets/listbox.cpp @@ -1,7 +1,7 @@ /* * A List Box Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) + * 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 @@ -134,6 +134,7 @@ namespace nana std::function weak_ordering; + std::shared_ptr font; ///< The exclusive column font column() = default; @@ -150,11 +151,12 @@ namespace nana index = other.index; alignment = other.alignment; weak_ordering = other.weak_ordering; + font = other.font; } return *this; } - + column(column&& other): caption(std::move(other.caption)), width_px(other.width_px), @@ -163,6 +165,7 @@ namespace nana index(other.index), alignment(other.alignment), weak_ordering(std::move(other.weak_ordering)), + font(std::move(other.font)), ess_(other.ess_) { } @@ -237,6 +240,12 @@ namespace nana //Definition is provided after essence void fit_content(unsigned maximize = 100000) noexcept override; + /// Sets an exclusive font for the column + void typeface(const paint::font& column_font) override; + + /// Returns a font + paint::font typeface() const noexcept override; + bool visible() const noexcept override { return visible_state; @@ -307,7 +316,12 @@ namespace nana return cont_.back().index; } - unsigned pixels() const noexcept ///< the visible width of the whole header + void clear() + { + cont_.clear(); + } + + unsigned width_px() const noexcept ///< the visible width of the whole header { unsigned pixels = 0; for(auto & col : cont_) @@ -716,6 +730,13 @@ namespace nana std::size_t column_pos; }; + enum class view_action + { + auto_view, + top_view, + bottom_view, + }; + class es_lister { public: @@ -776,8 +797,8 @@ namespace nana void emit_cs(const index_pair& pos, bool for_selection) { - item_proxy i(ess_, pos); - arg_listbox arg{ i }; + item_proxy item(ess_, pos); + arg_listbox arg{ item }; auto & events = wd_ptr()->events(); @@ -792,9 +813,9 @@ namespace nana if (p && (p->item_pos == pos)) { if (for_selection) - p->inline_ptr->notify_status(inline_widget_status::selecting, i.selected()); + p->inline_ptr->notify_status(inline_widget_status::selecting, item.selected()); else - p->inline_ptr->notify_status(inline_widget_status::checking, i.checked()); + p->inline_ptr->notify_status(inline_widget_status::checking, item.checked()); } } } @@ -933,7 +954,7 @@ namespace nana */ bool sort_column(std::size_t pos, const bool * reverse) { - if (npos == pos) + if (nana::npos == pos) { sort_attrs_.column = npos; return false; @@ -971,17 +992,12 @@ namespace nana return prstatus; } - void scroll(const index_pair& abs_pos, bool to_bottom); + /// Scroll the selected item into the view + void scroll_into_view(const index_pair& abs_pos, view_action vw_act); - /// Append a new category with a specified name and return a pointer to it. - category_t* create_cat(native_string_type&& text) - { - categories_.emplace_back(std::move(text)); - return &categories_.back(); - } /// will use the key to insert new cat before the first cat with compare less than the key, or at the end of the list of cat and return a ref to that new cat. ? - category_t* create_cat(std::shared_ptr& ptr) + category_t* create_category(std::shared_ptr& ptr) { //A workaround for old version of libstdc++ //Some operations of vector provided by libstdc++ don't accept const iterator. @@ -1012,9 +1028,15 @@ namespace nana return &(categories_.back()); } - /// add a new cat created at "pos" and return a ref to it - category_t* create_cat(std::size_t pos, native_string_type&& text) + /// Inserts a new category at position specified by pos + category_t* create_category(native_string_type&& text, std::size_t pos = nana::npos) { + if (::nana::npos == pos) + { + categories_.emplace_back(std::move(text)); + return &categories_.back(); + } + return &(*categories_.emplace(this->get(pos), std::move(text))); } @@ -1068,22 +1090,13 @@ namespace nana */ index_pair index_cast(const index_pair& from, bool from_display_order) const { - auto cat = get(from.cat); - if (from.item < cat->sorted.size()) - { - if (from_display_order) - return index_pair{ from.cat, static_cast(cat->sorted[from.item]) }; - - for (size_type i = 0; i < cat->sorted.size(); ++i) - { - if (from.item == cat->sorted[i]) - return index_pair{ from.cat, i }; - } - } - throw std::out_of_range("listbox: invalid item position"); + auto target = index_cast_noexcept(from, from_display_order); + if (target.empty()) + throw std::out_of_range("listbox: invalid element position"); + return target; } - index_pair index_cast_noexcpt(const index_pair& from, bool from_display_order, const index_pair& default_value = index_pair{npos, npos}) const noexcept + index_pair index_cast_noexcept(const index_pair& from, bool from_display_order, const index_pair& default_value = index_pair{npos, npos}) const noexcept { if (from.cat < categories_.size()) { @@ -1273,6 +1286,7 @@ namespace nana } //Backward + n = -n; dpos = pos; if (good(dpos.cat)) { @@ -1516,11 +1530,37 @@ namespace nana return n; } - template - std::vector> select_display_range_if(const index_pair& fr_abs, index_pair to_dpl, bool deselect_others, Pred pred) + /// Finds a good item or category if an item specified by pos is invaild + index_pair find_next_good(index_pair pos, bool ignore_category) const noexcept { - const index_pairs already_selected = (deselect_others ? this->pick_items(true) : index_pairs{}); + //Return the pos if it is good + if (this->good(pos)) + return pos; + + while(pos.cat < this->categories_.size()) + { + if ((pos.npos == pos.item) && !ignore_category) + return pos; + auto cat_item_size = this->get(pos.cat)->items.size(); + + if (pos.item < cat_item_size) + return pos; + + if ((pos.npos == pos.item) && (cat_item_size > 0) && ignore_category) + return index_pair{ pos.cat, 0 }; + + ++pos.cat; + pos.item = pos.npos; + } + return index_pair{}; + } + + template + std::vector> select_display_range_if(index_pair fr_abs, index_pair to_dpl, bool deselect_others, Pred pred) + { + fr_abs = find_next_good(fr_abs, true); + if (to_dpl.empty()) { if (fr_abs.empty()) @@ -1528,11 +1568,17 @@ namespace nana to_dpl = this->last(); } + auto fr_dpl = (fr_abs.is_category() ? fr_abs : this->index_cast(fr_abs, false)); //Converts an absolute position to display position if (fr_dpl > to_dpl) std::swap(fr_dpl, to_dpl); + if (to_dpl.is_category() && this->size_item(to_dpl.cat) > 0) + to_dpl.item = this->size_item(to_dpl.cat) - 1; + + const index_pairs already_selected = (deselect_others ? this->pick_items(true) : index_pairs{}); + const auto begin = fr_dpl; const auto last = to_dpl; @@ -1586,7 +1632,7 @@ namespace nana return pairs; } - bool select_for_all(bool sel, const index_pair& except = index_pair{npos, npos}) + bool select_for_all(bool sel, const index_pair& except_abs = index_pair{npos, npos}) { bool changed = false; index_pair pos; @@ -1595,7 +1641,7 @@ namespace nana pos.item = 0; for(auto & m : cat.items) { - if (except != pos) + if (except_abs != pos) { if (m.flags.selected != sel) { @@ -1618,18 +1664,33 @@ namespace nana } /// return absolute positions, no relative to display - index_pairs pick_items(bool for_selection) const + /** + * @param for_selection Indicates whether the selected items or checked items to be returned. + * @param find_first Indicates whether or not to return the first item which + * @param items_status a pointer refers to a bool object to receive the status whethe the picked items are all selected or all checked, in contrast to for_selection + */ + index_pairs pick_items(bool for_selection, bool find_first = false, bool * items_status = nullptr) const { index_pairs results; index_pair id; + if (items_status) + *items_status = true; + for (auto & cat : categories_) { id.item = 0; for (auto & m : cat.items) { if (for_selection ? m.flags.selected : m.flags.checked) + { + if (items_status && *items_status) + *items_status = (for_selection ? m.flags.checked : m.flags.selected); + results.push_back(id); // absolute positions, no relative to display + if (find_first) + return results; + } ++id.item; } ++id.cat; @@ -1637,34 +1698,9 @@ namespace nana return results; } - /// return absolute positions, no relative to display - bool item_selected_all_checked(index_pairs& vec) const - { - index_pair id; - bool ck = true; - - for (auto & cat : categories_) - { - id.item = 0; - for (auto & m : cat.items) - { - if (m.flags.selected) - { - vec.push_back(id); // absolute positions, no relative to display - ck &= m.flags.checked; - } - ++id.item; - } - ++id.cat; - } - - //Just returns true when the all selected items are checked. - return ck; - } - ///items.at(abs_pos.item).flags; + return (for_selection ? flags.selected : flags.checked); + } + void resize_disp_area() { auto head_px = this->header_visible_px(); @@ -2010,8 +2055,7 @@ namespace nana index_pair first_display() const noexcept { - auto offset_display = content_view->origin().y / item_height(); - return lister.advance(lister.first(), offset_display); + return lister.advance(lister.first(), static_cast(content_view->origin().y / item_height())); } unsigned item_height() const noexcept @@ -2035,19 +2079,6 @@ namespace nana return from + orignal.position() - origin; } - struct pred_mouse_selection - { - index_pair pos_; - pred_mouse_selection(index_pair pos) noexcept - : pos_(pos) - {} - - bool operator()(const std::pair& m) const noexcept - { - return (pos_ == m.first); - } - }; - std::pair columns_range() const { rectangle r; @@ -2055,7 +2086,7 @@ namespace nana return{}; auto origin = content_view->origin(); - return{ r.x - origin.x, r.x - origin.x + static_cast(header.pixels()) }; + return{ r.x - origin.x, r.x - origin.x + static_cast(header.width_px()) }; } void start_mouse_selection(const arg_mouse& arg) @@ -2095,7 +2126,7 @@ namespace nana auto content_x = coordinate_cast({ columns_range().first, 0 }, true).x; if ((std::max)(mouse_selection.end_position.x, mouse_selection.begin_position.x) >= content_x && - (std::min)(mouse_selection.end_position.x, mouse_selection.begin_position.x) < content_x + static_cast(header.pixels())) + (std::min)(mouse_selection.end_position.x, mouse_selection.begin_position.x) < content_x + static_cast(header.width_px())) { auto const begin_off = (std::max)((std::min)(mouse_selection.begin_position.y, mouse_selection.end_position.y), 0) / item_height(); @@ -2141,9 +2172,12 @@ namespace nana #endif { - if (selections.cend() == std::find_if(selections.cbegin(), selections.cend(), pred_mouse_selection{ *i })) + auto & selpos = *i; + if (selections.cend() == std::find_if(selections.cbegin(), selections.cend(), /*pred_mouse_selection{ *i }*/[&selpos](const std::pair& m){ + return (selpos == m.first); + })) { - item_proxy{ this, *i }.select(false); + item_proxy{ this, selpos }.select(false); i = mouse_selection.selections.erase(i); } else @@ -2208,7 +2242,7 @@ namespace nana ::nana::size calc_content_size(bool try_update = true) { size ctt_size( - this->header.pixels() + this->header.margin(), + this->header.width_px() + this->header.margin(), static_cast(this->lister.the_number_of_expanded()) * this->item_height() ); @@ -2224,7 +2258,7 @@ namespace nana int item_xpos(const nana::rectangle& r) const { - auto seq = header_seq(r.width); + auto seq = ordered_columns(r.width); if (seq.empty()) return 0; @@ -2254,12 +2288,12 @@ namespace nana { /// we are inside auto const origin = content_view->origin(); - if(header.visible() && (pos.y < static_cast(scheme_ptr->header_height) + area.y)) + if (header.visible() && (pos.y < static_cast(header_visible_px()) + area.y)) { /// we are in the header new_where.first = parts::header; new_where.second = this->column_from_pos(pos.x); } - else if (area.x <= pos.x + origin.x && pos.x + origin.x < area.x + static_cast(header.pixels())) + else if (area.x <= pos.x + origin.x && pos.x + origin.x < area.x + static_cast(header.width_px())) { // detect if cursor is in the area of header margin if (pos.x < area.x - origin.x + static_cast(header.margin())) @@ -2339,9 +2373,42 @@ namespace nana return r; } + double header_font_px() const + { + unsigned max_font_px = 0; + + paint::graphics graph{ size{ 1, 1 } }; + for (auto & col : this->header.cont()) + { + if (!col.visible()) + continue; + + graph.typeface(col.typeface()); + + unsigned as, ds, ileading; + graph.text_metrics(as, ds, ileading); + + max_font_px = (std::max)(as + ds, max_font_px); + } + + if (0 == max_font_px) + { + graph.typeface(listbox_ptr->typeface()); + unsigned as, ds, ileading; + graph.text_metrics(as, ds, ileading); + + max_font_px = (std::max)(as + ds, max_font_px); + } + + return max_font_px; + } + unsigned header_visible_px() const { - return (header.visible() ? scheme_ptr->header_height : 0); + if (!header.visible()) + return 0; + + return scheme_ptr->header_padding_top + scheme_ptr->header_padding_bottom + static_cast(header_font_px()); } bool rect_header(nana::rectangle& r) const @@ -2350,7 +2417,7 @@ namespace nana { r = this->content_area(); - r.height = scheme_ptr->header_height; + r.height = header_visible_px(); if (lister.wd_ptr()->borderless()) return !r.empty(); @@ -2390,7 +2457,7 @@ namespace nana return true; } - std::vector header_seq(unsigned lister_w) const + std::vector ordered_columns(unsigned lister_w) const { std::vector seqs; int x = -content_view->origin().x; @@ -2402,10 +2469,12 @@ namespace nana x += col.width_px; if (x > 0) - seqs.push_back(col.index); + { + if (x >= static_cast(lister_w)) + break; - if (x >= static_cast(lister_w)) - break; + seqs.push_back(col.index); + } } return seqs; } @@ -2760,6 +2829,29 @@ namespace nana _m_refresh(); } + + /// Sets an exclusive font for the column + void es_header::column::typeface(const paint::font& column_font) + { + this->font.reset(new paint::font{ column_font }); + + API::refresh_window(*ess_->listbox_ptr); + } + + /// Returns a font + paint::font es_header::column::typeface() const noexcept + { + //Returns the exclusive font if it is not empty + if (this->font && !this->font->empty()) + return *this->font; + + //Returns the column font if it is not empty + if (ess_->scheme_ptr->column_font && !ess_->scheme_ptr->column_font) + return *(ess_->scheme_ptr->column_font); + + //If all above fonts are invalid, returns the widget font. + return ess_->listbox_ptr->typeface(); + } //end es_header::column functions class inline_indicator @@ -2848,7 +2940,7 @@ namespace nana std::vector> panes_; }; - void es_lister::scroll(const index_pair& abs_pos, bool to_bottom) + void es_lister::scroll_into_view(const index_pair& abs_pos, view_action vw_act) { auto& cat = *get(abs_pos.cat); @@ -2868,13 +2960,24 @@ namespace nana } auto origin = ess_->content_view->origin(); - origin.y = 0; auto off = this->distance(this->first(), this->index_cast(abs_pos, false)) * ess_->item_height(); auto screen_px = ess_->content_view->view_area().height; - if (to_bottom) + if (view_action::auto_view == vw_act) + { + if (static_cast(off) < origin.y) + vw_act = view_action::top_view; + else if (static_cast(off) >= static_cast(origin.y + ess_->content_view->view_area().height)) + vw_act = view_action::bottom_view; + else + return; + } + + origin.y = 0; + + if (view_action::bottom_view == vw_act) { off += ess_->item_height(); if (off >= screen_px) @@ -2889,7 +2992,7 @@ namespace nana origin.y = static_cast(last_off - screen_px); } - if(ess_->content_view->move_origin(origin - ess_->content_view->origin())) + if (ess_->content_view->move_origin(origin - ess_->content_view->origin())) ess_->content_view->sync(false); } @@ -2911,9 +3014,9 @@ namespace nana } } - void es_lister::move_select(bool upwards, bool unselect_previous, bool /*trace_selected*/) noexcept + void es_lister::move_select(bool upwards, bool unselect_previous, bool into_view) noexcept { - auto next_selected_dpl = index_cast_noexcpt(latest_selected_abs, false); //convert absolute position to display position + auto next_selected_dpl = index_cast_noexcept(latest_selected_abs, false); //convert absolute position to display position if (next_selected_dpl.empty()) // has no cat ? (cat == npos) => beging from first cat { @@ -2991,6 +3094,9 @@ namespace nana } else break; } + + if (into_view && !latest_selected_abs.empty()) + this->scroll_into_view(latest_selected_abs, view_action::auto_view); } std::string es_lister::to_string(const export_options& exp_opt) const @@ -3064,6 +3170,14 @@ namespace nana using item_state = essence::item_state; using parts = essence::parts; + struct column_rendering_parameter + { + unsigned margin; + unsigned height; + double max_font_px; + paint::font wdg_font; + }; + drawer_header_impl(essence* es) noexcept: essence_(es){} size_type splitter() const noexcept @@ -3088,15 +3202,13 @@ namespace nana { if(col.visible_state) { - auto col_pixels = static_cast(col.width_px); - - if ((col_pixels < x + static_cast(essence_->scheme_ptr->header_splitter_area_before)) - && (x < col_pixels + static_cast(essence_->scheme_ptr->header_splitter_area_after))) + if ((static_cast(col.width_px) < x + static_cast(essence_->scheme_ptr->header_splitter_area_before)) + && (x < static_cast(col.width_px) + static_cast(essence_->scheme_ptr->header_splitter_area_after))) { grabs_.splitter = col.index; // original index return true; } - x -= col_pixels; + x -= static_cast(col.width_px); } } } @@ -3175,25 +3287,31 @@ namespace nana 0, r.height - 1 }; + column_rendering_parameter crp; + //The first item includes the margin - unsigned margin = essence_->header.margin(); + crp.margin = essence_->header.margin(); + + crp.height = essence_->header_visible_px(); + crp.max_font_px = essence_->header_font_px(); + crp.wdg_font = graph.typeface(); for (auto & col : essence_->header.cont()) { if (col.visible_state) { - column_r.width = col.width_px + margin; + column_r.width = col.width_px + crp.margin; const auto right_pos = column_r.right(); //Make sure the column is in the display area. if (right_pos > r.x) { - _m_draw_header_item(graph, margin, column_r, text_color, col, (col.index == essence_->pointer_where.second ? state : item_state::normal)); + _m_draw_header_item(graph, crp, column_r, text_color, col, (col.index == essence_->pointer_where.second ? state : item_state::normal)); graph.line({ right_pos - 1, r.y }, { right_pos - 1, r.bottom() - 2 }, border_color); } - margin = 0; + crp.margin = 0; column_r.x = right_pos; if (right_pos > r.right()) @@ -3258,7 +3376,7 @@ namespace nana return npos; } - void _m_draw_header_item(graph_reference graph, unsigned margin, const rectangle& column_r, const ::nana::color& fgcolor, const es_header::column& column, item_state state) + void _m_draw_header_item(graph_reference graph, const column_rendering_parameter& crp, const rectangle& column_r, const ::nana::color& fgcolor, const es_header::column& column, item_state state) { ::nana::color bgcolor; @@ -3289,7 +3407,16 @@ namespace nana { graph.palette(true, fgcolor); - point text_pos{ column_r.x + static_cast(margin), (static_cast(essence_->scheme_ptr->header_height) - static_cast(essence_->text_height)) / 2 }; + //Set column font + graph.typeface(column.typeface()); + + unsigned ascent, descent, ileading; + graph.text_metrics(ascent, descent, ileading); + + point text_pos{ + column_r.x + static_cast(crp.margin), + column_r.bottom() - static_cast(crp.height + ascent + descent) / 2 + }; if (align::left == column.alignment) text_pos.x += text_margin; @@ -3297,6 +3424,9 @@ namespace nana text_margin = 0; text_aligner.draw(column.caption, text_pos, column_r.width - text_margin); + + //Restores widget font + graph.typeface(crp.wdg_font); } auto & sort = essence_->lister.sort_attrs(); @@ -3313,16 +3443,20 @@ namespace nana { const auto & col = essence_->header.at(essence_->pointer_where.second); - auto margin = 0; - - if (&essence_->header.at(0, true) == &col) - margin = essence_->header.margin(); + column_rendering_parameter crp; + crp.margin = 0; + crp.height = essence_->header_visible_px(); + crp.max_font_px = essence_->header_font_px(); + crp.wdg_font = essence_->listbox_ptr->typeface(); - paint::graphics fl_graph({ col.width_px + margin, essence_->scheme_ptr->header_height }); + if (&essence_->header.at(0, true) == &col) + crp.margin = essence_->header.margin(); + + paint::graphics fl_graph({ col.width_px + crp.margin, crp.height }); fl_graph.typeface(essence_->graph->typeface()); - _m_draw_header_item(fl_graph, margin, rectangle{ fl_graph.size()}, colors::white, col, item_state::floated); + _m_draw_header_item(fl_graph, crp, rectangle{ fl_graph.size()}, colors::white, col, item_state::floated); auto xpos = essence_->header.range(col.index).first + pos.x - grabs_.start_pos; essence_->graph->blend(rectangle{ point{ xpos - essence_->content_view->origin().x + rect.x, rect.y } , fl_graph.size() }, fl_graph, {}, 0.5); @@ -3381,7 +3515,7 @@ namespace nana essence_->graph->palette(false, bgcolor); - auto const header_w = essence_->header.pixels(); + auto const header_w = essence_->header.width_px(); auto const item_height_px = essence_->item_height(); auto const origin = essence_->content_view->origin(); @@ -3416,6 +3550,8 @@ namespace nana rect.y - static_cast(origin.y % item_height_px) }; + essence_->inline_buffered_table.swap(essence_->inline_table); + // The first display is empty when the listbox is empty. if (!first_disp.empty()) { @@ -3427,19 +3563,17 @@ namespace nana hoverred_pos = lister.advance(first_disp, static_cast(ptr_where.second)); } - auto subitems = essence_->header_seq(rect.width); + auto const columns = essence_->ordered_columns(rect.width); - if (subitems.empty()) + if (columns.empty()) return; - int txtoff = essence_->scheme_ptr->item_height_ex / 2; + auto const txtoff = static_cast(essence_->scheme_ptr->item_height_ex) / 2; auto i_categ = lister.get(first_disp.cat); auto idx = first_disp; - essence_->inline_buffered_table.swap(essence_->inline_table); - for (auto & cat : lister.cat_container()) for (auto & ind : cat.indicators) { @@ -3464,7 +3598,7 @@ namespace nana auto item_pos = lister.index_cast(index_pair{ idx.cat, offs }, true); //convert display position to absolute position - _m_draw_item(*i_categ, item_pos, item_coord, txtoff, header_w, rect, subitems, bgcolor, fgcolor, + _m_draw_item(*i_categ, item_pos, item_coord, txtoff, header_w, rect, columns, bgcolor, fgcolor, (hoverred_pos == idx ? item_state::highlighted : item_state::normal) ); @@ -3498,7 +3632,7 @@ namespace nana auto item_pos = lister.index_cast(index_pair{ idx.cat, pos }, true); //convert display position to absolute position - _m_draw_item(*i_categ, item_pos, item_coord, txtoff, header_w, rect, subitems, bgcolor, fgcolor, + _m_draw_item(*i_categ, item_pos, item_coord, txtoff, header_w, rect, columns, bgcolor, fgcolor, (idx == hoverred_pos ? item_state::highlighted : item_state::normal) ); @@ -3509,10 +3643,10 @@ namespace nana ++idx.item; } } - - essence_->inline_buffered_table.clear(); } + essence_->inline_buffered_table.clear(); + if (item_coord.y < rect.bottom()) { rectangle bground_r{ rect.x, item_coord.y, rect.width, static_cast(rect.bottom() - item_coord.y) }; @@ -3521,7 +3655,7 @@ namespace nana } //Draw mouse selection - //Check if the mouse selection box is present. + //Check if the mouse selection box presents. if (essence_->mouse_selection.begin_position != essence_->mouse_selection.end_position) { point box_position{ @@ -3870,21 +4004,11 @@ namespace nana void _m_draw_item_border(int item_top) const { //Draw selecting inner rectangle - /* - rectangle r{ x, y, width, essence_->item_height() }; - - essence_->graph->rectangle(r, false, static_cast(0x99defd)); - - essence_->graph->palette(false, colors::white); - paint::draw(*essence_->graph).corner(r, 1); - - essence_->graph->rectangle(r.pare_off(1), false); - */ rectangle r{ essence_->content_area().x - essence_->content_view->origin().x + static_cast(essence_->header.margin()), item_top, - essence_->header.pixels(), + essence_->header.width_px(), essence_->item_height() }; @@ -3949,8 +4073,6 @@ namespace nana void trigger::typeface_changed(graph_reference graph) { - //essence_->text_height = graph.text_extent_size(L"jHWn0123456789/text_height = 0; unsigned as, ds, il; if (graph.text_metrics(as, ds, il)) @@ -4101,7 +4223,7 @@ namespace nana { auto * item_ptr = (item_pos.is_category() ? nullptr : &lister.at(item_pos)); - const auto abs_item_pos = lister.index_cast_noexcpt(item_pos, true, item_pos); //convert display position to absolute position + const auto abs_item_pos = lister.index_cast_noexcept(item_pos, true, item_pos); //convert display position to absolute position if(ptr_where.first == parts::list) { @@ -4117,17 +4239,16 @@ namespace nana essence_->content_view->sync(false); } - bool sel = true; + bool new_selected_status = true; - //no single selected - if (!lister.single_status(true)) + if (!lister.single_status(true)) //multiply selection enabled { if (arg.shift) { //Set the first item as the begin of selected item if there //is not a latest selected item.(#154 reported by RenaudAlpes) - if (lister.latest_selected_abs.empty() || lister.latest_selected_abs.is_category()) - lister.latest_selected_abs.set_both(0); + if (lister.latest_selected_abs.empty()) + lister.latest_selected_abs = lister.first(); auto before = lister.latest_selected_abs; @@ -4141,7 +4262,7 @@ namespace nana else if (arg.ctrl) { essence_->mouse_selection.reverse_selection = true; - sel = !item_proxy(essence_, abs_item_pos).selected(); + new_selected_status = !essence_->cs_status(abs_item_pos, true); } else { @@ -4150,12 +4271,12 @@ namespace nana //Unselects all selected items if the current item is not selected before selecting. auto selected = lister.pick_items(true); if (selected.cend() == std::find(selected.cbegin(), selected.cend(), item_pos)) - lister.select_for_all(false, item_pos); + lister.select_for_all(false, abs_item_pos); } else { //Unselects all selected items except current item if right button clicked. - lister.select_for_all(false, item_pos); //cancel all selections + lister.select_for_all(false, abs_item_pos); //cancel all selections } } } @@ -4164,14 +4285,14 @@ namespace nana //Clicking on a category is ignored when single selection is enabled. //Fixed by Greentwip(issue #121) if (item_ptr) - sel = !item_proxy(essence_, abs_item_pos).selected(); + new_selected_status = !item_proxy(essence_, abs_item_pos).selected(); } if(item_ptr) { - if (item_ptr->flags.selected != sel) + if (item_ptr->flags.selected != new_selected_status) { - if (sel) + if (new_selected_status) { //Deselects the previously selected item. lister.cancel_others_if_single_enabled(true, abs_item_pos); @@ -4180,13 +4301,15 @@ namespace nana else if (essence_->lister.latest_selected_abs == abs_item_pos) essence_->lister.latest_selected_abs.set_both(npos); - item_ptr->flags.selected = sel; + item_ptr->flags.selected = new_selected_status; lister.emit_cs(abs_item_pos, true); } } else { - lister.cat_status(item_pos.cat, true, true); + //A category was clicked. Sets all child items to be selected only if multiply selection is enabled. + if(!lister.single_status(true)) + lister.cat_status(item_pos.cat, true, true); } } else @@ -4336,8 +4459,9 @@ namespace nana void trigger::key_press(graph_reference graph, const arg_keyboard& arg) { + auto & list = essence_->lister; // Exit if list is empty - if (essence_->lister.first().empty()) + if (list.first().empty()) return; bool upward = false; @@ -4347,44 +4471,81 @@ namespace nana case keyboard::os_arrow_up: upward = true; case keyboard::os_arrow_down: - essence_->lister.move_select(upward, !arg.shift, true); + list.move_select(upward, !arg.shift, true); break; case L' ': { - index_pairs s; - bool ck = ! essence_->lister.item_selected_all_checked(s); - for(auto i : s) - item_proxy(essence_, i).check(ck); + bool items_checked{ false }; + auto items = list.pick_items(true, false, &items_checked); + items_checked = !items_checked; + for (auto i : items) + item_proxy(essence_, i).check(items_checked); } break; case keyboard::os_pageup : upward = true; case keyboard::os_pagedown: { - //Turns page, then returns if no change occurs - if (!essence_->content_view->turn_page(!upward, false)) - return; + auto const item_px = essence_->item_height(); + auto picked_items = list.pick_items(true, true); + index_pair init_idx = (picked_items.empty() ? list.first() : picked_items[0]); essence_->lister.select_for_all(false); - auto idx = essence_->first_display(); + //Get the pixels between the init item and top edge or bottom edge + auto logic_top = static_cast(list.distance(list.first(), init_idx) * item_px); - if (!upward) - idx = essence_->lister.advance(idx, static_cast(essence_->count_of_exposed(false)) - 1); + auto const screen_top = essence_->content_view->origin().y; + auto const screen_bottom = screen_top + essence_->content_view->view_area().height; + index_pair target_idx; - if (!idx.is_category()) - item_proxy::from_display(essence_, idx).select(true); - else if (!essence_->lister.single_status(true)) //not selected - essence_->lister.cat_status(idx.cat, true, true); + //Check if it scrolls in current screen window + //condition: top of target item is not less than top edge of content view and + //the bottom of target item is not greater than bottom edge of content view. + if ((screen_top + static_cast(item_px) <= logic_top) && (logic_top + static_cast(item_px) + static_cast(item_px) <= static_cast(screen_bottom))) + { + int offset = (static_cast(upward ? screen_top : screen_bottom - item_px) - logic_top) / static_cast(item_px); + target_idx = list.advance(init_idx, offset); + } + else + { + //turn page + auto page_item_count = (std::max)(1, static_cast(essence_->count_of_exposed(false))); - break; + auto origin = essence_->content_view->origin(); + if (upward) + { + target_idx = list.advance(init_idx, -page_item_count); + if (target_idx.empty()) + target_idx = list.first(); + + origin.y = static_cast(list.distance(list.first(), target_idx) * item_px); + } + else + { + target_idx = list.advance(init_idx, page_item_count); + if (target_idx.empty()) + target_idx = list.last(); + + origin.y = static_cast((list.distance(list.first(), target_idx) + 1) * item_px); + origin.y = (std::max)(origin.y - static_cast(screen_bottom - screen_top), 0); + } + + essence_->content_view->move_origin(origin - essence_->content_view->origin()); + } + + if (!target_idx.is_category()) + item_proxy::from_display(essence_, target_idx).select(true); + else if (!list.single_status(true)) //not selected + list.cat_status(target_idx.cat, true, true); } + break; case keyboard::os_home: case keyboard::os_end: { - essence_->lister.select_for_all(false); + list.select_for_all(false); - auto pos = (keyboard::os_home == arg.key ? essence_->lister.first() : essence_->lister.last()); + auto pos = (keyboard::os_home == arg.key ? list.first() : list.last()); if (!pos.empty()) { //When the pos indicates an empty category, then search forwards/backwards(depending on arg.key whether it is Home or End) for a non empty category. @@ -4393,9 +4554,9 @@ namespace nana { if (keyboard::os_home == arg.key) { - while (0 == essence_->lister.size_item(pos.cat)) + while (0 == list.size_item(pos.cat)) { - if (++pos.cat >= essence_->lister.cat_container().size()) + if (++pos.cat >= list.cat_container().size()) { pos = index_pair{ npos, npos }; break; @@ -4404,7 +4565,7 @@ namespace nana } else { - while (0 == essence_->lister.size_item(pos.cat)) + while (0 == list.size_item(pos.cat)) { if (pos.cat-- == 0) { @@ -4416,7 +4577,7 @@ namespace nana if (!pos.empty()) { - if (essence_->lister.expand(pos.cat)) + if (list.expand(pos.cat)) pos.item = 0; } } @@ -4425,13 +4586,16 @@ namespace nana { if (pos.is_category()) { - if (!essence_->lister.single_status(true)) //multiple selection is not enabled - essence_->lister.cat_status(pos.cat, true, true); + if (!list.single_status(true)) //multiple selection is not enabled + list.cat_status(pos.cat, true, true); } else item_proxy::from_display(essence_, pos).select(true); + + list.scroll_into_view(pos, view_action::auto_view); } } + } break; default: @@ -4527,7 +4691,7 @@ namespace nana ess_->lister.get(pos_.cat)->expand = false; if (!this->displayed()) - ess_->lister.scroll(pos_, !(ess_->first_display() > this->to_display())); + ess_->lister.scroll_into_view(pos_, (ess_->first_display() > this->to_display() ? view_action::top_view : view_action::bottom_view)); } ess_->update(); @@ -4565,7 +4729,7 @@ namespace nana ess_->lister.latest_selected_abs.set_both(npos); if (scroll_view && (!this->displayed())) - ess_->lister.scroll(pos_, !(ess_->first_display() > this->to_display())); + ess_->lister.scroll_into_view(pos_, (ess_->first_display() > this->to_display() ? view_action::top_view : view_action::bottom_view)); ess_->update(); return *this; @@ -4662,12 +4826,12 @@ namespace nana bool item_proxy::operator==(const std::string& s) const { - return (text(pos_.item) == s); + return (text(0) == s); } bool item_proxy::operator==(const std::wstring& s) const { - return (text(pos_.item) == to_utf8(s)); + return (text(0) == to_utf8(s)); } item_proxy & item_proxy::operator=(const item_proxy& rhs) @@ -5158,7 +5322,8 @@ namespace nana cat_->make_sort_order(); ess_->lister.sort(); - ess_->update(true); + //Don't ignore the auto-draw flag for performance enhancement. + ess_->update(); } } //end class cat_proxy @@ -5232,13 +5397,13 @@ namespace nana else pos.item = ess.lister.size_item(cat_pos) ? 0 : ::nana::npos; - ess.lister.scroll(pos, to_bottom); - ess.update(); + this->scroll(to_bottom, pos); } void listbox::scroll(bool to_bottom, const index_pair& abs_pos) { - _m_ess().lister.scroll(abs_pos, to_bottom); + using view_action = drawerbase::listbox::view_action; + _m_ess().lister.scroll_into_view(abs_pos, (to_bottom ? view_action::bottom_view : view_action::top_view)); _m_ess().update(); } @@ -5260,11 +5425,20 @@ namespace nana return pos; } + void listbox::clear_headers() + { + internal_scope_guard lock; + auto & ess = _m_ess(); + ess.lister.erase(); + ess.header.clear(); + ess.update(); + } + listbox::cat_proxy listbox::append(std::string s) { internal_scope_guard lock; auto & ess = _m_ess(); - auto new_cat_ptr = ess.lister.create_cat(to_nstring(std::move(s))); + auto new_cat_ptr = ess.lister.create_category(to_nstring(std::move(s))); ess.update(); return cat_proxy{ &ess, new_cat_ptr }; @@ -5274,7 +5448,7 @@ namespace nana { internal_scope_guard lock; auto & ess = _m_ess(); - auto new_cat_ptr = ess.lister.create_cat(to_nstring(std::move(s))); + auto new_cat_ptr = ess.lister.create_category(to_nstring(std::move(s))); ess.update(); return cat_proxy{ &ess, new_cat_ptr }; } @@ -5285,7 +5459,7 @@ namespace nana auto & ess = _m_ess(); for (auto & arg : categories) - ess.lister.create_cat(native_string_type(to_nstring(arg))); + ess.lister.create_category(native_string_type(to_nstring(arg))); ess.update(); } @@ -5295,7 +5469,7 @@ namespace nana auto & ess = _m_ess(); for (auto & arg : categories) - ess.lister.create_cat(native_string_type(to_nstring(arg))); + ess.lister.create_category(native_string_type(to_nstring(arg))); ess.update(); } @@ -5312,7 +5486,7 @@ namespace nana { internal_scope_guard lock; auto & ess = _m_ess(); - auto new_cat_ptr = ess.lister.create_cat(cat.position(), to_nstring(std::move(str))); + auto new_cat_ptr = ess.lister.create_category(to_nstring(std::move(str)), cat.position()); return cat_proxy{ &ess, new_cat_ptr }; } @@ -5320,7 +5494,7 @@ namespace nana { internal_scope_guard lock; auto & ess = _m_ess(); - auto new_cat_ptr = ess.lister.create_cat(cat.position(), to_nstring(std::move(str))); + auto new_cat_ptr = ess.lister.create_category(to_nstring(std::move(str)), cat.position()); return cat_proxy{ &ess, new_cat_ptr }; } @@ -5610,7 +5784,7 @@ namespace nana void listbox::move_select(bool upwards) ///key_ptr = ptr; } ess.update(); @@ -5720,5 +5893,11 @@ namespace nana } } } + + std::shared_ptr listbox::_m_scroll_operation() + { + internal_scope_guard lock; + return _m_ess().content_view->scroll_operation(); + } //end class listbox }//end namespace nana diff --git a/source/gui/widgets/menubar.cpp b/source/gui/widgets/menubar.cpp index 5071c094..3c211ba6 100644 --- a/source/gui/widgets/menubar.cpp +++ b/source/gui/widgets/menubar.cpp @@ -625,6 +625,13 @@ namespace nana { return get_drawer_trigger().ess().items.size(); } + + void menubar::clear() + { + internal_scope_guard lock; + get_drawer_trigger().ess().items.clear(); + API::refresh_window(handle()); + } bool menubar::cancel() { diff --git a/source/gui/widgets/progress.cpp b/source/gui/widgets/progress.cpp index 2c498f7c..8a679c4c 100644 --- a/source/gui/widgets/progress.cpp +++ b/source/gui/widgets/progress.cpp @@ -1,6 +1,6 @@ /* * A Progress Indicator Implementation - * Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) + * 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 @@ -96,7 +96,11 @@ namespace nana { if (widget_) { - auto value_px = (widget_->size().width - border_px * 2) * value_ / max_; + auto value_px = (widget_->size().width - border_px * 2); + + //avoid overflow + if (value_ < max_) + value_px = static_cast(value_px * (double(value_) / double(max_))); if (value_px != value_px_) { diff --git a/source/gui/widgets/scroll.cpp b/source/gui/widgets/scroll.cpp index b59e4721..acc2c8b6 100644 --- a/source/gui/widgets/scroll.cpp +++ b/source/gui/widgets/scroll.cpp @@ -1,7 +1,7 @@ /* * A Scroll Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2014 Jinhao(cnjinhao@hotmail.com) + * 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 @@ -20,9 +20,15 @@ namespace nana namespace scroll { //struct metrics_type - metrics_type::metrics_type() - :peak(1), range(1), step(1), value(0), - what(buttons::none), pressed(false), scroll_length(0), scroll_pos(0) + metrics_type::metrics_type(): + peak(1), + range(1), + step(1), + value(0), + what(buttons::none), + pressed(false), + scroll_length(0), + scroll_pos(0) {} //end struct metrics_type diff --git a/source/gui/widgets/skeletons/content_view.cpp b/source/gui/widgets/skeletons/content_view.cpp index b279dc21..b7d8f722 100644 --- a/source/gui/widgets/skeletons/content_view.cpp +++ b/source/gui/widgets/skeletons/content_view.cpp @@ -1,7 +1,7 @@ /* * A Content View Implementation * Nana C++ Library(http://www.nanapro.org) -* Copyright(C) 2017 Jinhao(cnjinhao@hotmail.com) +* Copyright(C) 2017-2018 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -19,6 +19,39 @@ namespace nana { namespace widgets { namespace skeletons { + struct cv_scroll_rep + { + content_view::scrolls enabled_scrolls{ content_view::scrolls::both }; + nana::scroll horz; + nana::scroll vert; + + scroll_interface* scroll(arg_wheel::wheel whl) + { + if (arg_wheel::wheel::horizontal == whl) + return &horz; + else if (arg_wheel::wheel::vertical == whl) + return | + return nullptr; + } + }; + + class scroll_operation + : public scroll_operation_interface + { + public: + scroll_operation(std::shared_ptr& impl) + : cv_scroll_(impl) + { + } + private: + bool visible(bool vert) const override + { + return !(vert ? cv_scroll_->vert.empty() : cv_scroll_->horz.empty()); + } + private: + std::shared_ptr const cv_scroll_; + }; + struct content_view::implementation { content_view& view; @@ -35,9 +68,13 @@ namespace nana { bool drag_started{ false }; point origin; - scrolls enabled_scrolls{scrolls::both}; + /* + scrolls enabled_scrolls{scrolls::both}; //deprecated nana::scroll horz; nana::scroll vert; + */ + + std::shared_ptr cv_scroll; timer tmr; @@ -50,9 +87,11 @@ namespace nana { implementation(content_view& v, window handle) : view(v), - window_handle(handle) + window_handle(handle), + cv_scroll(std::make_shared()) { API::events(handle).mouse_wheel.connect_unignorable([this](const arg_wheel& arg) { +#if 0 scroll_interface * scroll = nullptr; switch (arg.which) { @@ -66,6 +105,11 @@ namespace nana { //Other button is not unsupported. return; } +#else + auto const scroll = cv_scroll->scroll(arg.which); + if (nullptr == scroll) + return; +#endif if (!API::empty_window(arg.window_handle)) { @@ -159,11 +203,11 @@ namespace nana { auto speed_horz = 0; if (skew.x) - speed_horz = skew.x / (std::max)(1, static_cast(horz.step())) + (skew.x < 0 ? -1 : 1); + speed_horz = skew.x / (std::max)(1, static_cast(cv_scroll->horz.step())) + (skew.x < 0 ? -1 : 1); auto speed_vert = 0; if (skew.y) - speed_vert = skew.y / (std::max)(1, static_cast(vert.step())) + (skew.y < 0 ? -1 : 1); + speed_vert = skew.y / (std::max)(1, static_cast(cv_scroll->vert.step())) + (skew.y < 0 ? -1 : 1); speed_horz = (std::min)(5, (std::max)(speed_horz, -5)); speed_vert = (std::min)(5, (std::max)(speed_vert, -5)); @@ -180,10 +224,10 @@ namespace nana { //event hander for scrollbars auto event_fn = [this](const arg_scroll& arg) { - if (arg.window_handle == this->vert.handle()) - origin.y = static_cast(this->vert.value()); + if (arg.window_handle == cv_scroll->vert.handle()) + origin.y = static_cast(cv_scroll->vert.value()); else - origin.x = static_cast(this->horz.value()); + origin.x = static_cast(cv_scroll->horz.value()); if (this->events.scrolled) this->events.scrolled(); @@ -194,33 +238,33 @@ namespace nana { this->passive = passive; - bool const vert_allowed = (enabled_scrolls == scrolls::vert || enabled_scrolls == scrolls::both); - bool const horz_allowed = (enabled_scrolls == scrolls::horz || enabled_scrolls == scrolls::both); + bool const vert_allowed = (cv_scroll->enabled_scrolls == scrolls::vert || cv_scroll->enabled_scrolls == scrolls::both); + bool const horz_allowed = (cv_scroll->enabled_scrolls == scrolls::horz || cv_scroll->enabled_scrolls == scrolls::both); if ((imd_area.width != disp_area.width) && vert_allowed) { - if (vert.empty()) + if (cv_scroll->vert.empty()) { - vert.create(window_handle); - vert.events().value_changed.connect_unignorable(event_fn); - API::take_active(vert, false, window_handle); + cv_scroll->vert.create(window_handle); + cv_scroll->vert.events().value_changed.connect_unignorable(event_fn); + API::take_active(cv_scroll->vert, false, window_handle); this->passive = false; } - vert.move({ + cv_scroll->vert.move({ disp_area.x + static_cast(imd_area.width) + skew_vert.x, disp_area.y + skew_vert.y, space(), imd_area.height + extra_px.height }); - vert.amount(content_size.height); - vert.range(imd_area.height); - vert.value(origin.y); + cv_scroll->vert.amount(content_size.height); + cv_scroll->vert.range(imd_area.height); + cv_scroll->vert.value(origin.y); } else { - vert.close(); + cv_scroll->vert.close(); //If vert is allowed, it indicates the vertical origin is not moved //Make sure the v origin is zero @@ -230,28 +274,28 @@ namespace nana { if ((imd_area.height != disp_area.height) && horz_allowed) { - if (horz.empty()) + if (cv_scroll->horz.empty()) { - horz.create(window_handle); - horz.events().value_changed.connect_unignorable(event_fn); - API::take_active(horz, false, window_handle); + cv_scroll->horz.create(window_handle); + cv_scroll->horz.events().value_changed.connect_unignorable(event_fn); + API::take_active(cv_scroll->horz, false, window_handle); this->passive = false; } - horz.move({ + cv_scroll->horz.move({ disp_area.x + skew_horz.x, disp_area.y + static_cast(imd_area.height) + skew_horz.y, imd_area.width + extra_px.width, space() }); - horz.amount(content_size.width); - horz.range(imd_area.width); - horz.value(origin.x); + cv_scroll->horz.amount(content_size.width); + cv_scroll->horz.range(imd_area.width); + cv_scroll->horz.value(origin.x); } else { - horz.close(); + cv_scroll->horz.close(); //If horz is allowed, it indicates the horzontal origin is not moved //Make sure the x origin is zero if (horz_allowed) @@ -279,20 +323,25 @@ namespace nana { bool content_view::enable_scrolls(scrolls which) { - if (impl_->enabled_scrolls == which) + if (impl_->cv_scroll->enabled_scrolls == which) return false; - impl_->enabled_scrolls = which; + impl_->cv_scroll->enabled_scrolls = which; impl_->size_changed(false); return true; } + std::shared_ptr content_view::scroll_operation() const + { + return std::make_shared(impl_->cv_scroll); + } + void content_view::step(unsigned step_value, bool horz) { if (horz) - impl_->horz.step(step_value); + impl_->cv_scroll->horz.step(step_value); else - impl_->vert.step(step_value); + impl_->cv_scroll->vert.step(step_value); } bool content_view::scroll(bool forwards, bool horz) @@ -306,17 +355,17 @@ namespace nana { } if (horz) - return impl_->horz.make_step(forwards, speed); + return impl_->cv_scroll->horz.make_step(forwards, speed); - return impl_->vert.make_step(forwards, speed); + return impl_->cv_scroll->vert.make_step(forwards, speed); } bool content_view::turn_page(bool forwards, bool horz) { if (horz) - return impl_->horz.make_page_scroll(forwards); + return impl_->cv_scroll->horz.make_page_scroll(forwards); else - return impl_->vert.make_page_scroll(forwards); + return impl_->cv_scroll->vert.make_page_scroll(forwards); } void content_view::disp_area(const rectangle& da, const point& skew_horz, const point& skew_vert, const size& extra_px, bool try_update) @@ -404,7 +453,7 @@ namespace nana { void content_view::draw_corner(graph_reference graph) { auto r = corner(); - if ((!r.empty()) && (scrolls::both == impl_->enabled_scrolls)) + if ((!r.empty()) && (scrolls::both == impl_->cv_scroll->enabled_scrolls)) graph.rectangle(r, true, colors::button_face); } @@ -415,8 +464,8 @@ namespace nana { rectangle content_view::view_area(const size& alt_content_size) const { - bool const vert_allowed = (impl_->enabled_scrolls == scrolls::vert || impl_->enabled_scrolls == scrolls::both); - bool const horz_allowed = (impl_->enabled_scrolls == scrolls::horz || impl_->enabled_scrolls == scrolls::both); + bool const vert_allowed = (impl_->cv_scroll->enabled_scrolls == scrolls::vert || impl_->cv_scroll->enabled_scrolls == scrolls::both); + bool const horz_allowed = (impl_->cv_scroll->enabled_scrolls == scrolls::horz || impl_->cv_scroll->enabled_scrolls == scrolls::both); unsigned extra_horz = (horz_allowed && (impl_->disp_area.width < alt_content_size.width) ? space() : 0); unsigned extra_vert = (vert_allowed && (impl_->disp_area.height < alt_content_size.height + extra_horz) ? space() : 0); @@ -435,13 +484,13 @@ namespace nana { unsigned content_view::extra_space(bool horz) const { - return ((horz ? impl_->horz.empty() : impl_->vert.empty()) ? 0 : space()); + return ((horz ? impl_->cv_scroll->horz.empty() : impl_->cv_scroll->vert.empty()) ? 0 : space()); } void content_view::change_position(int pos, bool aligned, bool horz) { if (aligned) - pos -= (pos % static_cast(horz ? impl_->horz.step() : impl_->vert.step())); + pos -= (pos % static_cast(horz ? impl_->cv_scroll->horz.step() : impl_->cv_scroll->vert.step())); auto imd_size = this->view_area(); @@ -490,8 +539,8 @@ namespace nana { void content_view::sync(bool passive) { impl_->passive = passive; - impl_->horz.value(impl_->origin.x); - impl_->vert.value(impl_->origin.y); + impl_->cv_scroll->horz.value(impl_->origin.x); + impl_->cv_scroll->vert.value(impl_->origin.y); impl_->passive = true; } @@ -520,15 +569,15 @@ namespace nana { impl_->origin.y = 0; bool changed = false; - if (!impl_->horz.empty() && (static_cast(impl_->horz.value()) != impl_->origin.x)) + if (!impl_->cv_scroll->horz.empty() && (static_cast(impl_->cv_scroll->horz.value()) != impl_->origin.x)) { - impl_->horz.value(impl_->origin.x); + impl_->cv_scroll->horz.value(impl_->origin.x); changed = true; } - if ((!impl_->vert.empty()) && (static_cast(impl_->vert.value()) != impl_->origin.y)) + if ((!impl_->cv_scroll->vert.empty()) && (static_cast(impl_->cv_scroll->vert.value()) != impl_->origin.y)) { - impl_->vert.value(impl_->origin.y); + impl_->cv_scroll->vert.value(impl_->origin.y); changed = true; } diff --git a/source/gui/widgets/skeletons/content_view.hpp b/source/gui/widgets/skeletons/content_view.hpp index c5bcc3da..40ee84df 100644 --- a/source/gui/widgets/skeletons/content_view.hpp +++ b/source/gui/widgets/skeletons/content_view.hpp @@ -1,7 +1,7 @@ /* * A Content View Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2017 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2017-2018 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -16,6 +16,7 @@ #include #include +#include namespace nana { @@ -58,6 +59,8 @@ namespace skeletons bool enable_scrolls(scrolls which); + std::shared_ptr scroll_operation() const; + void step(unsigned step_value, bool horz); bool scroll(bool forwards, bool horz); bool turn_page(bool forwards, bool horz); @@ -92,7 +95,7 @@ namespace skeletons return 16; } private: - implementation * const impl_; + implementation* const impl_; }; } } diff --git a/source/gui/widgets/skeletons/text_editor.cpp b/source/gui/widgets/skeletons/text_editor.cpp index fefaf1ed..6ee30fb5 100644 --- a/source/gui/widgets/skeletons/text_editor.cpp +++ b/source/gui/widgets/skeletons/text_editor.cpp @@ -1,7 +1,7 @@ /* * A text editor implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) + * 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 @@ -1954,6 +1954,11 @@ namespace nana{ namespace widgets return impl_->capacities.behavior->take_lines(); } + std::shared_ptr text_editor::scroll_operation() const + { + return impl_->cview->scroll_operation(); + } + void text_editor::draw_corner() { impl_->cview->draw_corner(graph_); diff --git a/source/gui/widgets/textbox.cpp b/source/gui/widgets/textbox.cpp index de6e2dd9..265a8e28 100644 --- a/source/gui/widgets/textbox.cpp +++ b/source/gui/widgets/textbox.cpp @@ -1,7 +1,7 @@ /* * A Textbox Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) + * 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 @@ -816,6 +816,16 @@ namespace drawerbase { if(editor) editor->reset_caret_pixels(); } + + std::shared_ptr textbox::_m_scroll_operation() const + { + internal_scope_guard lock; + auto editor = get_drawer_trigger().editor(); + if (editor) + return editor->scroll_operation(); + + return {}; + } //end class textbox }//end namespace nana diff --git a/source/gui/widgets/toolbar.cpp b/source/gui/widgets/toolbar.cpp index 953e4796..963b99cf 100644 --- a/source/gui/widgets/toolbar.cpp +++ b/source/gui/widgets/toolbar.cpp @@ -1,496 +1,516 @@ -/* - * A Toolbar Implementation - * 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/widgets/toolbar.cpp - * @contributors: - * kmribti(pr#105) - */ - -#include -#include - -#include - -namespace nana -{ - arg_toolbar::arg_toolbar(toolbar& tbar, std::size_t btn) - : widget(tbar), button{btn} - {} - - namespace drawerbase - { - 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: - using container_type = std::vector; - using size_type = container_type::size_type; - - ~item_container() - { - for(auto ptr : cont_) - delete ptr; - } - - void insert(size_type pos, std::string text, const nana::paint::image& img, item_type::kind type) - { - item_type* m = new item_type(std::move(text), img, type); - - if(pos < cont_.size()) - cont_.insert(cont_.begin() + pos, m); - else - cont_.push_back(m); - } - - void push_back(const std::string& text, const nana::paint::image& img) - { - insert(cont_.size(), text, img, item_type::kind::button); - } - - void push_back(const std::string& text) - { - insert(cont_.size(), text, nana::paint::image(), item_type::kind::button); - } - - //Contributed by kmribti(pr#105) - void go_right() noexcept - { - right_ = cont_.size(); - } - - //Contributed by kmribti(pr#105) - size_t right() const noexcept - { - return right_; - } - - void insert(size_type pos) - { - if(pos < cont_.size()) - cont_.insert(cont_.begin() + pos, static_cast(nullptr)); //both works in C++0x and C++2003 - else - cont_.push_back(nullptr); - } - - void separate() - { - cont_.push_back(nullptr); - } - - size_type size() const noexcept - { - return cont_.size(); - } - - container_type& container() noexcept - { - return cont_; - } - - item_type * at(size_type pos) - { - return cont_.at(pos); - } - private: - container_type cont_; - size_t right_{ npos }; - }; - - class item_renderer - { - public: - 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) - {} - - void operator()(int x, int y, unsigned width, unsigned height, item_type& item, state_t state) - { - //draw background - if (state != state_t::normal) - { - nana::rectangle background_r(x, y, width, height); - graph.rectangle(background_r, false, static_cast(0x3399FF)); - - if (state_t::highlighted == state || state_t::selected == state) - graph.gradual_rectangle(background_r.pare_off(1), bgcolor, static_cast(state_t::selected == state ? 0x99CCFF : 0xC0DDFC), true); - } - - if(!item.image.empty()) - { - auto imgsize = item.image.size(); - - if (imgsize.width > scale) imgsize.width = scale; - if (imgsize.height > scale) imgsize.height = scale; - - nana::point pos( - x + static_cast(scale + extra_size - imgsize.width) / 2, - y + static_cast(height - imgsize.height) / 2); - - item.image.paste(::nana::rectangle{ imgsize }, graph, pos); - if(item.enable == false) - { - nana::paint::graphics gh(imgsize); - gh.bitblt(::nana::rectangle{ imgsize }, graph, pos); - gh.rgb_to_wb(); - gh.paste(graph, pos.x, pos.y); - } - else if (state == state_t::normal) - { - graph.blend(nana::rectangle(pos, imgsize), ::nana::color(0xc0, 0xdd, 0xfc).blend(bgcolor, 0.5), 0.25); - } - - x += scale; - width -= scale; - } - - if(textout) - { - graph.string({ x + static_cast(width - item.textsize.width) / 2, y + static_cast(height - item.textsize.height) / 2 }, item.text); - } - } - - protected: - nana::paint::graphics& graph; - bool textout; - unsigned scale; - ::nana::color bgcolor; - }; - - struct drawer::drawer_impl_type - { - event_handle event_size{ nullptr }; - paint::graphics* graph_ptr{ nullptr }; - - unsigned scale{16}; - bool textout{false}; - size_type which{npos}; - item_renderer::state_t state{item_renderer::state_t::normal}; - - item_container items; - ::nana::tooltip tooltip; - }; - - //class drawer - drawer::drawer() - : impl_(new drawer_impl_type) - { - } - - drawer::~drawer() - { - delete impl_; - } - - item_container& drawer::items() const - { - return impl_->items; - } - - void drawer::scale(unsigned s) - { - impl_->scale = s; - - for(auto m : impl_->items.container()) - _m_calc_pixels(m, true); - } - - void drawer::refresh(graph_reference graph) - { - int x = 2, y = 2; - - auto bgcolor = API::bgcolor(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); - size_type index = 0; - - for (auto item : impl_->items.container()) - { - if (item) - { - _m_calc_pixels(item, false); - item->position = x; - ir(x, y, item->pixels, impl_->scale + ir.extra_size, *item, (index == impl_->which ? impl_->state : item_renderer::state_t::normal)); - x += item->pixels; - } - else - { - x += 2; - graph.line({ x, y + 2 }, { x, y + static_cast(impl_->scale + ir.extra_size) - 4 }, static_cast(0x808080)); - x += 4; - } - ++index; - - //Reset the x position of items which are right aligned - //Contributed by kmribti(pr#105) - if (index == impl_->items.right() && index < impl_->items.size()) - { - unsigned total_x = 0; - for (size_t i = index; i < impl_->items.size(); i++) { - if (impl_->items.at(i) == nullptr) { - total_x += 8; // we assume that separator has width = 8. - } - else { - _m_calc_pixels(impl_->items.at(i), false); - total_x += impl_->items.at(i)->pixels; - } - } - - x = graph.size().width - total_x - 4; - } - } - } - - void drawer::attached(widget_reference widget, graph_reference graph) - { - impl_->graph_ptr = &graph; - - widget_ = static_cast< ::nana::toolbar*>(&widget); - widget.caption("nana toolbar"); - - if (widget_->detached()) return; - - impl_->event_size = API::events(widget.parent()).resized.connect_unignorable([this](const arg_resized& arg) - { - auto wd = widget_->handle(); - API::window_size(wd, nana::size(arg.width, widget_->size().height)); - API::update_window(wd); - }); - } - - void drawer::detached() - { - API::umake_event(impl_->event_size); - impl_->event_size = nullptr; - impl_->graph_ptr = nullptr; - } - - void drawer::mouse_move(graph_reference graph, const arg_mouse& arg) - { - if (arg.left_button) - return; - - size_type which = _m_which(arg.pos, true); - if(impl_->which != which) - { - auto & container = impl_->items.container(); - if (impl_->which != npos && container.at(impl_->which)->enable) - { - ::nana::arg_toolbar arg{ *widget_, impl_->which }; - widget_->events().leave.emit(arg, widget_->handle()); - } - - impl_->which = which; - if (which == npos || container.at(which)->enable) - { - impl_->state = item_renderer::state_t::highlighted; - - refresh(graph); - API::dev::lazy_refresh(); - - if (impl_->state == item_renderer::state_t::highlighted) - { - ::nana::arg_toolbar arg{ *widget_, which }; - widget_->events().enter.emit(arg, widget_->handle()); - } - } - - if(which != npos) - impl_->tooltip.show(widget_->handle(), nana::point(arg.pos.x, arg.pos.y + 20), (*(container.begin() + which))->text, 0); - else - impl_->tooltip.close(); - } - } - - void drawer::mouse_leave(graph_reference graph, const arg_mouse&) - { - if(impl_->which != npos) - { - size_type which = impl_->which; - - impl_->which = npos; - refresh(graph); - API::dev::lazy_refresh(); - - if (which != npos && impl_->items.at(which)->enable) - { - ::nana::arg_toolbar arg{ *widget_, which }; - widget_->events().leave.emit(arg, widget_->handle()); - } - } - impl_->tooltip.close(); - } - - void drawer::mouse_down(graph_reference graph, const arg_mouse&) - { - impl_->tooltip.close(); - if(impl_->which != npos && (impl_->items.at(impl_->which)->enable)) - { - impl_->state = item_renderer::state_t::selected; - refresh(graph); - API::dev::lazy_refresh(); - } - } - - void drawer::mouse_up(graph_reference graph, const arg_mouse& arg) - { - if(impl_->which != npos) - { - size_type which = _m_which(arg.pos, false); - if(impl_->which == which) - { - ::nana::arg_toolbar arg{ *widget_, which }; - widget_->events().selected.emit(arg, widget_->handle()); - - impl_->state = item_renderer::state_t::highlighted; - } - else - { - impl_->which = which; - impl_->state = (which == npos ? item_renderer::state_t::normal : item_renderer::state_t::highlighted); - } - - refresh(graph); - API::dev::lazy_refresh(); - } - } - - drawer::size_type drawer::_m_which(point pos, bool want_if_disabled) const - { - if (pos.x < 2 || pos.y < 2 || pos.y >= static_cast(impl_->scale + item_renderer::extra_size + 2)) return npos; - - pos.x -= 2; - - std::size_t index = 0; - for(auto m: impl_->items.container()) - { - unsigned x = static_cast(pos.x); - if (m && x >= m->position && x <= (m->position+m->pixels)) - return (((!m) || (!m->enable && !want_if_disabled)) ? npos : index); - - ++index; - } - return npos; - } - - void drawer::_m_calc_pixels(item_type* item, bool force) - { - if (item && (force || (0 == item->pixels))) - { - if (item->text.size()) - item->textsize = impl_->graph_ptr->text_extent_size(item->text); - - if (item->image.empty() == false) - item->pixels = impl_->scale + item_renderer::extra_size; - - if (item->textsize.width && impl_->textout) - item->pixels += item->textsize.width + 8; - } - } - //class drawer - }//end namespace toolbar - }//end namespace drawerbase - - //class toolbar - toolbar::toolbar(window wd, bool visible, bool detached) : - detached_(detached) - { - create(wd, rectangle(), visible); - } - - toolbar::toolbar(window wd, const rectangle& r, bool visible, bool detached) : - detached_(detached) - { - create(wd, r, visible); - } - - //Contributed by kmribti(pr#105) - void toolbar::go_right() - { - get_drawer_trigger().items().go_right(); - } - - void toolbar::separate() - { - get_drawer_trigger().items().separate(); - API::refresh_window(handle()); - } - - void toolbar::append(const std::string& text, const nana::paint::image& img) - { - get_drawer_trigger().items().push_back(text, img); - API::refresh_window(handle()); - } - - void toolbar::append(const std::string& text) - { - get_drawer_trigger().items().push_back(text, {}); - API::refresh_window(this->handle()); - } - - bool toolbar::enable(size_type pos) const - { - auto & items = get_drawer_trigger().items(); - - if (items.size() <= pos) - return false; - - auto m = items.at(pos); - return (m && m->enable); - } - - void toolbar::enable(size_type pos, bool eb) - { - auto & items = get_drawer_trigger().items(); - - if (items.size() > pos) - { - auto m = items.at(pos); - if (m && (m->enable != eb)) - { - m->enable = eb; - API::refresh_window(this->handle()); - } - } - } - - void toolbar::scale(unsigned s) - { - get_drawer_trigger().scale(s); - API::refresh_window(handle()); - } - //end class toolbar -}//end namespace nana +/* + * A Toolbar Implementation + * 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/widgets/toolbar.cpp + * @contributors: + * kmribti(pr#105) + */ + +#include +#include + +#include + +namespace nana +{ + arg_toolbar::arg_toolbar(toolbar& tbar, std::size_t btn) + : widget(tbar), button{btn} + {} + + namespace drawerbase + { + 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: + using container_type = std::vector; + using size_type = container_type::size_type; + + ~item_container() + { + clear(); + } + + void insert(size_type pos, std::string text, const nana::paint::image& img, item_type::kind type) + { + item_type* m = new item_type(std::move(text), img, type); + + if(pos < cont_.size()) + cont_.insert(cont_.begin() + pos, m); + else + cont_.push_back(m); + } + + void push_back(const std::string& text, const nana::paint::image& img) + { + insert(cont_.size(), text, img, item_type::kind::button); + } + + void push_back(const std::string& text) + { + insert(cont_.size(), text, nana::paint::image(), item_type::kind::button); + } + + //Contributed by kmribti(pr#105) + void go_right() noexcept + { + right_ = cont_.size(); + } + + //Contributed by kmribti(pr#105) + size_t right() const noexcept + { + return right_; + } + + void insert(size_type pos) + { + if(pos < cont_.size()) + cont_.insert(cont_.begin() + pos, static_cast(nullptr)); //both works in C++0x and C++2003 + else + cont_.push_back(nullptr); + } + + void separate() + { + cont_.push_back(nullptr); + } + + size_type size() const noexcept + { + return cont_.size(); + } + + container_type& container() noexcept + { + return cont_; + } + + item_type * at(size_type pos) + { + return cont_.at(pos); + } + + void clear() + { + for(auto ptr : cont_) + delete ptr; + + cont_.clear(); + } + + private: + container_type cont_; + size_t right_{ npos }; + }; + + class item_renderer + { + public: + 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) + {} + + void operator()(int x, int y, unsigned width, unsigned height, item_type& item, state_t state) + { + //draw background + if (state != state_t::normal) + { + nana::rectangle background_r(x, y, width, height); + graph.rectangle(background_r, false, static_cast(0x3399FF)); + + if (state_t::highlighted == state || state_t::selected == state) + graph.gradual_rectangle(background_r.pare_off(1), bgcolor, static_cast(state_t::selected == state ? 0x99CCFF : 0xC0DDFC), true); + } + + if(!item.image.empty()) + { + auto imgsize = item.image.size(); + + if (imgsize.width > scale) imgsize.width = scale; + if (imgsize.height > scale) imgsize.height = scale; + + nana::point pos( + x + static_cast(scale + extra_size - imgsize.width) / 2, + y + static_cast(height - imgsize.height) / 2); + + item.image.paste(::nana::rectangle{ imgsize }, graph, pos); + if(item.enable == false) + { + nana::paint::graphics gh(imgsize); + gh.bitblt(::nana::rectangle{ imgsize }, graph, pos); + gh.rgb_to_wb(); + gh.paste(graph, pos.x, pos.y); + } + else if (state == state_t::normal) + { + graph.blend(nana::rectangle(pos, imgsize), ::nana::color(0xc0, 0xdd, 0xfc).blend(bgcolor, 0.5), 0.25); + } + + x += scale; + width -= scale; + } + + if(textout) + { + graph.string({ x + static_cast(width - item.textsize.width) / 2, y + static_cast(height - item.textsize.height) / 2 }, item.text); + } + } + + protected: + nana::paint::graphics& graph; + bool textout; + unsigned scale; + ::nana::color bgcolor; + }; + + struct drawer::drawer_impl_type + { + event_handle event_size{ nullptr }; + paint::graphics* graph_ptr{ nullptr }; + + unsigned scale{16}; + bool textout{false}; + size_type which{npos}; + item_renderer::state_t state{item_renderer::state_t::normal}; + + item_container items; + ::nana::tooltip tooltip; + }; + + //class drawer + drawer::drawer() + : impl_(new drawer_impl_type) + { + } + + drawer::~drawer() + { + delete impl_; + } + + item_container& drawer::items() const + { + return impl_->items; + } + + void drawer::scale(unsigned s) + { + impl_->scale = s; + + for(auto m : impl_->items.container()) + _m_calc_pixels(m, true); + } + + void drawer::refresh(graph_reference graph) + { + int x = 2, y = 2; + + auto bgcolor = API::bgcolor(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); + size_type index = 0; + + for (auto item : impl_->items.container()) + { + if (item) + { + _m_calc_pixels(item, false); + item->position = x; + ir(x, y, item->pixels, impl_->scale + ir.extra_size, *item, (index == impl_->which ? impl_->state : item_renderer::state_t::normal)); + x += item->pixels; + } + else + { + x += 2; + graph.line({ x, y + 2 }, { x, y + static_cast(impl_->scale + ir.extra_size) - 4 }, static_cast(0x808080)); + x += 4; + } + ++index; + + //Reset the x position of items which are right aligned + //Contributed by kmribti(pr#105) + if (index == impl_->items.right() && index < impl_->items.size()) + { + unsigned total_x = 0; + for (size_t i = index; i < impl_->items.size(); i++) { + if (impl_->items.at(i) == nullptr) { + total_x += 8; // we assume that separator has width = 8. + } + else { + _m_calc_pixels(impl_->items.at(i), false); + total_x += impl_->items.at(i)->pixels; + } + } + + x = graph.size().width - total_x - 4; + } + } + } + + void drawer::attached(widget_reference widget, graph_reference graph) + { + impl_->graph_ptr = &graph; + + widget_ = static_cast< ::nana::toolbar*>(&widget); + widget.caption("nana toolbar"); + + if (widget_->detached()) return; + + impl_->event_size = API::events(widget.parent()).resized.connect_unignorable([this](const arg_resized& arg) + { + auto wd = widget_->handle(); + API::window_size(wd, nana::size(arg.width, widget_->size().height)); + API::update_window(wd); + }); + } + + void drawer::detached() + { + API::umake_event(impl_->event_size); + impl_->event_size = nullptr; + impl_->graph_ptr = nullptr; + } + + void drawer::mouse_move(graph_reference graph, const arg_mouse& arg) + { + if (arg.left_button) + return; + + size_type which = _m_which(arg.pos, true); + if(impl_->which != which) + { + auto & container = impl_->items.container(); + if (impl_->which != npos && container.at(impl_->which)->enable) + { + ::nana::arg_toolbar arg{ *widget_, impl_->which }; + widget_->events().leave.emit(arg, widget_->handle()); + } + + impl_->which = which; + if (which == npos || container.at(which)->enable) + { + impl_->state = item_renderer::state_t::highlighted; + + refresh(graph); + API::dev::lazy_refresh(); + + if (impl_->state == item_renderer::state_t::highlighted) + { + ::nana::arg_toolbar arg{ *widget_, which }; + widget_->events().enter.emit(arg, widget_->handle()); + } + } + + if(which != npos) + impl_->tooltip.show(widget_->handle(), nana::point(arg.pos.x, arg.pos.y + 20), (*(container.begin() + which))->text, 0); + else + impl_->tooltip.close(); + } + } + + void drawer::mouse_leave(graph_reference graph, const arg_mouse&) + { + if(impl_->which != npos) + { + size_type which = impl_->which; + + impl_->which = npos; + refresh(graph); + API::dev::lazy_refresh(); + + if (which != npos && impl_->items.at(which)->enable) + { + ::nana::arg_toolbar arg{ *widget_, which }; + widget_->events().leave.emit(arg, widget_->handle()); + } + } + impl_->tooltip.close(); + } + + void drawer::mouse_down(graph_reference graph, const arg_mouse&) + { + impl_->tooltip.close(); + if(impl_->which != npos && (impl_->items.at(impl_->which)->enable)) + { + impl_->state = item_renderer::state_t::selected; + refresh(graph); + API::dev::lazy_refresh(); + } + } + + void drawer::mouse_up(graph_reference graph, const arg_mouse& arg) + { + if(impl_->which != npos) + { + size_type which = _m_which(arg.pos, false); + if(impl_->which == which) + { + ::nana::arg_toolbar arg{ *widget_, which }; + widget_->events().selected.emit(arg, widget_->handle()); + + impl_->state = item_renderer::state_t::highlighted; + } + else + { + impl_->which = which; + impl_->state = (which == npos ? item_renderer::state_t::normal : item_renderer::state_t::highlighted); + } + + refresh(graph); + API::dev::lazy_refresh(); + } + } + + drawer::size_type drawer::_m_which(point pos, bool want_if_disabled) const + { + if (pos.x < 2 || pos.y < 2 || pos.y >= static_cast(impl_->scale + item_renderer::extra_size + 2)) return npos; + + pos.x -= 2; + + std::size_t index = 0; + for(auto m: impl_->items.container()) + { + unsigned x = static_cast(pos.x); + if (m && x >= m->position && x <= (m->position+m->pixels)) + return (((!m) || (!m->enable && !want_if_disabled)) ? npos : index); + + ++index; + } + return npos; + } + + void drawer::_m_calc_pixels(item_type* item, bool force) + { + if (item && (force || (0 == item->pixels))) + { + if (item->text.size()) + item->textsize = impl_->graph_ptr->text_extent_size(item->text); + + if (item->image.empty() == false) + item->pixels = impl_->scale + item_renderer::extra_size; + + if (item->textsize.width && impl_->textout) + item->pixels += item->textsize.width + 8; + } + } + //class drawer + + // Item Proxy + void item_proxy::enable(bool enable_state) + { + widget.enable(button, enable_state); + } + }//end namespace toolbar + }//end namespace drawerbase + + //class toolbar + toolbar::toolbar(window wd, bool visible, bool detached) : + detached_(detached) + { + create(wd, rectangle(), visible); + } + + toolbar::toolbar(window wd, const rectangle& r, bool visible, bool detached) : + detached_(detached) + { + create(wd, r, visible); + } + + //Contributed by kmribti(pr#105) + void toolbar::go_right() + { + get_drawer_trigger().items().go_right(); + } + + void toolbar::separate() + { + get_drawer_trigger().items().separate(); + API::refresh_window(handle()); + } + + drawerbase::toolbar::item_proxy toolbar::append(const std::string& text, const nana::paint::image& img) + { + get_drawer_trigger().items().push_back(text, img); + API::refresh_window(handle()); + 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}; + } + + void toolbar::clear() + { + get_drawer_trigger().items().clear(); + API::refresh_window(this->handle()); + } + + bool toolbar::enable(size_type pos) const + { + auto & items = get_drawer_trigger().items(); + + if (items.size() <= pos) + return false; + + auto m = items.at(pos); + return (m && m->enable); + } + + void toolbar::enable(size_type pos, bool eb) + { + auto & items = get_drawer_trigger().items(); + + if (items.size() > pos) + { + auto m = items.at(pos); + if (m && (m->enable != eb)) + { + m->enable = eb; + API::refresh_window(this->handle()); + } + } + } + + void toolbar::scale(unsigned s) + { + get_drawer_trigger().scale(s); + API::refresh_window(handle()); + } + //end class toolbar +}//end namespace nana diff --git a/source/gui/widgets/treebox.cpp b/source/gui/widgets/treebox.cpp index 88468861..4dcf0d57 100644 --- a/source/gui/widgets/treebox.cpp +++ b/source/gui/widgets/treebox.cpp @@ -1,7 +1,7 @@ /* * A Treebox Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) + * 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 @@ -29,6 +29,25 @@ namespace nana { using node_type = trigger::node_type; + class exclusive_scroll_operation + : public scroll_operation_interface + { + public: + exclusive_scroll_operation(std::shared_ptr>& scroll_wdg) + :scroll_(scroll_wdg) + {} + + bool visible(bool vert) const override + { + if (vert) + return !scroll_->empty(); + + return false; + } + private: + std::shared_ptr> scroll_; + }; + bool no_sensitive_compare(const std::string& text, const char *pattern, std::size_t len) { if(len <= text.length()) @@ -214,7 +233,7 @@ namespace nana struct shape_tag { nana::upoint border; - nana::scroll scroll; + std::shared_ptr> scroll; mutable std::map image_table; @@ -261,6 +280,7 @@ namespace nana shape.first = nullptr; shape.indent_pixels = 10; shape.offset_x = 0; + shape.scroll = std::make_shared>(); attr.auto_draw = true; @@ -598,7 +618,7 @@ namespace nana std::size_t max_allow = max_allowed(); std::size_t visual_items = visual_item_size(); - auto & scroll = shape.scroll; + auto & scroll = *shape.scroll; if(visual_items <= max_allow) { if(!scroll.empty()) @@ -618,7 +638,7 @@ namespace nana adjust.scroll_timestamp = nana::system::timestamp(); adjust.timer.start(); - shape.first = attr.tree_cont.advance_if(nullptr, shape.scroll.value(), pred_allow_child{}); + shape.first = attr.tree_cont.advance_if(nullptr, shape.scroll->value(), pred_allow_child{}); draw(false, false, true); }); } @@ -641,7 +661,7 @@ namespace nana if(!data.graph) return 0; - return static_cast(data.graph->width() - (shape.scroll.empty() ? 0 : shape.scroll.size().width)); + return static_cast(data.graph->width() - (shape.scroll->empty() ? 0 : shape.scroll->size().width)); } unsigned node_w_pixels(const node_type *node) const @@ -958,10 +978,14 @@ namespace nana return *this; } + std::size_t item_proxy::size() const { std::size_t n = 0; - for(auto child = node_->child; child; child = child->child) + + //Fixed by ErrorFlynn + //this method incorrectly returned the number of levels beneath the nodes using child = child->child + for(auto child = node_->child; child; child = child->next) ++n; return n; @@ -1897,7 +1921,7 @@ namespace nana void trigger::mouse_wheel(graph_reference, const arg_wheel& arg) { - auto & scroll = impl_->shape.scroll; + auto & scroll = *impl_->shape.scroll; if (scroll.empty()) return; @@ -1931,10 +1955,10 @@ namespace nana impl_->draw(false); API::dev::lazy_refresh(); impl_->show_scroll(); - if(!impl_->shape.scroll.empty()) + if(!impl_->shape.scroll->empty()) { nana::size s = impl_->data.graph->size(); - impl_->shape.scroll.move(rectangle{ static_cast(s.width) - 16, 0, 16, s.height }); + impl_->shape.scroll->move(rectangle{ static_cast(s.width) - 16, 0, 16, s.height }); } } @@ -2209,5 +2233,11 @@ namespace nana { return item_proxy(const_cast(&get_drawer_trigger()), get_drawer_trigger().selected()); } + + std::shared_ptr treebox::_m_scroll_operation() const + { + internal_scope_guard lock; + return std::make_shared(get_drawer_trigger().impl()->shape.scroll); + } //end class treebox }//end namespace nana diff --git a/source/gui/widgets/widget.cpp b/source/gui/widgets/widget.cpp index 183768ab..3e3ab2cd 100644 --- a/source/gui/widgets/widget.cpp +++ b/source/gui/widgets/widget.cpp @@ -1,6 +1,6 @@ /* * The fundamental widget class implementation - * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) + * 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 @@ -158,6 +158,11 @@ namespace nana return (API::focus_window() == handle()); } + std::shared_ptr widget::scroll_operation() + { + return _m_scroll_operation(); + } + void widget::show() { _m_show(true); @@ -317,6 +322,11 @@ namespace nana API::window_enabled(handle(), value); } + std::shared_ptr widget::_m_scroll_operation() + { + return {}; + } + bool widget::_m_show(bool visible) { API::show_window(handle(), visible); diff --git a/source/paint/detail/image_jpeg.hpp b/source/paint/detail/image_jpeg.hpp index 1f19373b..d63a2c1d 100644 --- a/source/paint/detail/image_jpeg.hpp +++ b/source/paint/detail/image_jpeg.hpp @@ -91,7 +91,7 @@ namespace nana { ::jpeg_create_decompress(&jdstru); - ::jpeg_mem_src(&jdstru, const_cast(reinterpret_cast(data)), bytes); + ::jpeg_mem_src(&jdstru, const_cast(reinterpret_cast(data)), static_cast(bytes)); _m_read_jpg(jdstru); jpeg_finish_decompress(&jdstru); diff --git a/source/paint/graphics.cpp b/source/paint/graphics.cpp index 62f74016..c620f63d 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-2017 Jinhao(cnjinhao@hotmail.com) + * 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 @@ -127,11 +127,14 @@ namespace paint return impl_->real_font->family(); } - double font::size() const + double font::size(bool fixed) const { - if(empty()) return 0; + double size_pt = (empty() ? 0.0 : impl_->real_font->size()); - return impl_->real_font->size(); + if (fixed && (0.0 == size_pt)) + return platform_abstraction::font_default_pt(); + + return size_pt; } bool font::bold() const diff --git a/source/system/dataexch.cpp b/source/system/dataexch.cpp index 28674822..8c25ed5e 100644 --- a/source/system/dataexch.cpp +++ b/source/system/dataexch.cpp @@ -208,20 +208,20 @@ namespace nana{ namespace system{ memcpy(addr, buf, size); ::GlobalUnlock(g); - unsigned data_format; + unsigned data_format = CF_MAX; switch(fmt) { case format::text: data_format = CF_UNICODETEXT; break; case format::pixmap: data_format = CF_BITMAP; break; } - + res = (nullptr != ::SetClipboardData(data_format, g)); } ::CloseClipboard(); } #elif defined(NANA_X11) auto & spec = ::nana::detail::platform_spec::instance(); - + Atom atom_type; switch(fmt) { @@ -232,7 +232,7 @@ namespace nana{ namespace system{ spec.write_selection(owner, atom_type, buf, size); return true; - + #endif return res; } @@ -244,7 +244,7 @@ namespace nana{ namespace system{ #if defined(NANA_WINDOWS) if(::OpenClipboard(::GetFocus())) { - unsigned data_format; + unsigned data_format = CF_MAX; switch(fmt) { case format::text: data_format = CF_UNICODETEXT; break;