From 00c923df8f7dd2a69ecb66b277b0373722b7d820 Mon Sep 17 00:00:00 2001 From: King_DuckZ Date: Thu, 9 Nov 2017 23:04:52 +0000 Subject: [PATCH 001/149] Use target_compile_features instead of setting CXX_STANDARD. This allows child projects to just link to nana and automatically inherit the -std=c++14 setting. Minimum cmake version for this to work is 3.1. --- CMakeLists.txt | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1a43b5b5..24793395 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,8 +21,8 @@ # It seems that project() defines essential system variables like CMAKE_FIND_LIBRARY_PREFIXES. # https://bbs.archlinux.org/viewtopic.php?id=84967 -project(nana) -cmake_minimum_required(VERSION 2.8) +cmake_minimum_required(VERSION 3.1 FATAL_ERROR) +project(nana VERSION 1.5.5 LANGUAGES CXX) option(NANA_CMAKE_INSTALL_INCLUDES "Install nana includes when compile the library" ON) option(NANA_CMAKE_ENABLE_MINGW_STD_THREADS_WITH_MEGANZ "replaced boost.thread with meganz's mingw-std-threads." OFF) @@ -147,11 +147,9 @@ endif(UNIX) if(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") # Clang || GNU if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++14 -Wall -g") # Clang - + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -g") # Clang else ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall -g") # GNU - + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -g") # GNU endif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") endif(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") @@ -354,6 +352,19 @@ if(NANA_CMAKE_INSTALL_INCLUDES) message("The Nana include files will be installed in ${CMAKE_INSTALL_PREFIX}/include") endif(NANA_CMAKE_INSTALL_INCLUDES) +target_compile_features(${PROJECT_NAME} + PUBLIC cxx_nullptr + PUBLIC cxx_range_for + PUBLIC cxx_lambdas + PUBLIC cxx_decltype_auto + PUBLIC cxx_defaulted_functions + PUBLIC cxx_deleted_functions + PUBLIC cxx_auto_type + PUBLIC cxx_decltype_incomplete_return_types + PUBLIC cxx_defaulted_move_initializers + PUBLIC cxx_noexcept + PUBLIC cxx_rvalue_references +) # Just for information: message ("") From a09d23210f45f528abb38a950590346acbdf1d93 Mon Sep 17 00:00:00 2001 From: King_DuckZ Date: Thu, 9 Nov 2017 23:35:17 +0000 Subject: [PATCH 002/149] Use target_include_directories instead of include_directories. Also change the linked libraries where necessary and link to fontconfig, which fixes my build error. --- CMakeLists.txt | 110 +++++++++++++++++------------ cmake/Modules/FindFontconfig.cmake | 69 ++++++++++++++++++ 2 files changed, 134 insertions(+), 45 deletions(-) create mode 100644 cmake/Modules/FindFontconfig.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 24793395..d71d267b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,6 +22,7 @@ # https://bbs.archlinux.org/viewtopic.php?id=84967 cmake_minimum_required(VERSION 3.1 FATAL_ERROR) +list (APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules) project(nana VERSION 1.5.5 LANGUAGES CXX) option(NANA_CMAKE_INSTALL_INCLUDES "Install nana includes when compile the library" ON) @@ -64,6 +65,34 @@ if(POLICY CMP0004) # ignore leading space cmake_policy(SET CMP0004 OLD) endif() +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 ) +if(NANA_CMAKE_ENABLE_AUDIO) + list(APPEND NANA_SOURCE_SUBDIRS + /audio + /audio/detail ) +endif(NANA_CMAKE_ENABLE_AUDIO) +# 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}) + +add_library(${PROJECT_NAME} ${sources} ) ########### OS if(WIN32) @@ -120,6 +149,7 @@ if(APPLE) add_definitions(-DAPPLE) include_directories(/opt/X11/include/) list(APPEND NANA_LINKS -L/opt/X11/lib/ -liconv) + target_link_libraries(${PROJECT_NAME} PRIVATE iconv) set(ENABLE_AUDIO OFF) elseif(UNIX) add_definitions(-Dlinux) @@ -127,11 +157,28 @@ elseif(UNIX) endif(APPLE) if(UNIX) - list(APPEND NANA_LINKS -lX11) + find_package(X11 REQUIRED) + list(APPEND NANA_LINKS ${X11_LIBRARIES}) + target_link_libraries(${PROJECT_NAME} + PRIVATE ${X11_LIBRARIES} + PRIVATE ${X11_Xft_LIB} + ) + target_include_directories(${PROJECT_NAME} SYSTEM + PRIVATE ${X11_Xft_INCLUDE_PATH} + PRIVATE ${X11_INCLUDE_DIR} + ) find_package(Freetype) if (FREETYPE_FOUND) - include_directories( ${FREETYPE_INCLUDE_DIRS}) - list(APPEND NANA_LINKS -lXft) + find_package(Fontconfig REQUIRED) + target_include_directories(${PROJECT_NAME} SYSTEM + PRIVATE ${FREETYPE_INCLUDE_DIRS} + PRIVATE ${FONTCONFIG_INCLUDE_DIR} + ) + list(APPEND NANA_LINKS ${FREETYPE_LIBRARIES}) + target_link_libraries(${PROJECT_NAME} + PRIVATE ${FREETYPE_LIBRARIES} + PRIVATE ${FONTCONFIG_LIBRARIES} + ) endif(FREETYPE_FOUND) endif(UNIX) @@ -156,11 +203,14 @@ endif(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") # enable static linkage # GNU || CLang not MinGW if(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") # AND NOT MINGW + set(THREADS_PREFER_PTHREAD_FLAG ON) + find_package(Threads REQUIRED) + target_link_libraries(${PROJECT_NAME} PRIVATE Threads::Threads) if(NANA_CMAKE_SHARED_LIB) - list(APPEND NANA_LINKS -lgcc -lstdc++ -pthread) + list(APPEND NANA_LINKS -lgcc -lstdc++) else() - set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++ -pthread") + set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++") # message("Setting NANA_LINKS to -static-libgcc -static-libstdc++ -pthread or ${NANA_LINKS}") endif(NANA_CMAKE_SHARED_LIB) @@ -168,9 +218,7 @@ if(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") # AN # IS_GNUCXX < 5.3 else(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.3) - # set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lstdc++fs") # IS_GNUCXX 5.3 or more - list(APPEND NANA_LINKS -lstdc++fs) - + target_link_libraries(${PROJECT_NAME} PRIVATE stdc++fs) endif(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.3) endif(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") # AND NOT MINGW @@ -189,10 +237,9 @@ if(NANA_CMAKE_ENABLE_PNG) if(NANA_CMAKE_LIBPNG_FROM_OS) find_package(PNG) if (PNG_FOUND) - include_directories( ${PNG_INCLUDE_DIRS}) list(APPEND NANA_LINKS ${PNG_LIBRARIES}) - add_definitions("-DNANA_ENABLE_PNG" - "-DUSE_LIBPNG_FROM_OS") + target_include_directories(${PROJECT_NAME} PRIVATE PNG::PNG) + target_compile_definitions(${PROJECT_NAME} PUBLIC USE_LIBPNG_FROM_OS) endif(PNG_FOUND) else() add_definitions(-DNANA_ENABLE_PNG) @@ -205,10 +252,10 @@ if(NANA_CMAKE_ENABLE_JPEG) if(NANA_CMAKE_LIBJPEG_FROM_OS) find_package(JPEG) if (JPEG_FOUND) - include_directories( ${JPEG_INCLUDE_DIR}) - list(APPEND NANA_LINKS ${JPEG_LIBRARY}) - add_definitions("-DNANA_ENABLE_JPEG" - "-DUSE_LIBJPEG_FROM_OS") + target_include_directories(${PROJECT_NAME PRIVATE ${JPEG_INCLUDE_DIR}) + list(APPEND NANA_LINKS ${JPEG_LIBRARIES}) + target_include_directories(${PROJECT_NAME} PRIVATE ${JPEG_LIBRARIES}) + target_compile_definitions(${PROJECT_NAME} PUBLIC USE_LIBJPEG_FROM_OS) endif(JPEG_FOUND) else() add_definitions(-DNANA_ENABLE_JPEG) @@ -221,8 +268,9 @@ if(NANA_CMAKE_ENABLE_AUDIO) if(UNIX) find_package(ASOUND) if (ASOUND_FOUND) - include_directories( ${ASOUND_INCLUDE_DIRS}) + target_include_directories(${PROJECT_NAME PRIVATE ${ASOUND_INCLUDE_DIRS}) list(APPEND NANA_LINKS -lasound) + target_link_libraries(${PROJECT_NAME} PRIVATE ${ASOUND_LIBRARIES}) else(ASOUND_FOUND) message(FATAL_ERROR "libasound is not found") endif(ASOUND_FOUND) @@ -245,8 +293,8 @@ elseif (NANA_CMAKE_FIND_BOOST_FILESYSTEM OR NANA_CMAKE_BOOST_FILESYSTEM_FORCE) find_package(Boost COMPONENTS filesystem) if (Boost_FOUND) add_definitions(-DNANA_BOOST_FILESYSTEM_AVAILABLE) - include_directories(SYSTEM "${Boost_INCLUDE_DIR}") list(APPEND NANA_LINKS ${Boost_LIBRARIES}) ###### FIRST !!!!!!!!!!!!!!!!! add is not first + target_link_libraries(${PROJECT_NAME} PUBLIC Boost::Boost) endif (Boost_FOUND) set(Boost_USE_STATIC_LIBS ON) set(Boost_USE_STATIC_RUNTIME ON) # ?? @@ -267,33 +315,6 @@ endif(NANA_CMAKE_AUTOMATIC_GUI_TESTING) ####################### Main setting of Nana sources, targets and install -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 ) -if(NANA_CMAKE_ENABLE_AUDIO) - list(APPEND NANA_SOURCE_SUBDIRS - /audio - /audio/detail ) -endif(NANA_CMAKE_ENABLE_AUDIO) -# 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}) - if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") add_definitions(-fmax-errors=3) endif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") @@ -305,7 +326,6 @@ else() endif(NANA_CMAKE_SHARED_LIB) target_include_directories(${PROJECT_NAME} PUBLIC ${NANA_INCLUDE_DIR}) -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) diff --git a/cmake/Modules/FindFontconfig.cmake b/cmake/Modules/FindFontconfig.cmake new file mode 100644 index 00000000..e6fa81d8 --- /dev/null +++ b/cmake/Modules/FindFontconfig.cmake @@ -0,0 +1,69 @@ +# - Try to find the Fontconfig +# Once done this will define +# +# FONTCONFIG_FOUND - system has Fontconfig +# FONTCONFIG_INCLUDE_DIR - The include directory to use for the fontconfig headers +# FONTCONFIG_LIBRARIES - Link these to use FONTCONFIG +# FONTCONFIG_DEFINITIONS - Compiler switches required for using FONTCONFIG + +# Copyright (c) 2006,2007 Laurent Montel, +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +if (FONTCONFIG_LIBRARIES AND FONTCONFIG_INCLUDE_DIR) + + # in cache already + set(FONTCONFIG_FOUND TRUE) + +else (FONTCONFIG_LIBRARIES AND FONTCONFIG_INCLUDE_DIR) + + if (NOT WIN32) + # use pkg-config to get the directories and then use these values + # in the FIND_PATH() and FIND_LIBRARY() calls + find_package(PkgConfig) + pkg_check_modules(PC_FONTCONFIG fontconfig) + + set(FONTCONFIG_DEFINITIONS ${PC_FONTCONFIG_CFLAGS_OTHER}) + endif (NOT WIN32) + + find_path(FONTCONFIG_INCLUDE_DIR fontconfig/fontconfig.h + PATHS + ${PC_FONTCONFIG_INCLUDEDIR} + ${PC_FONTCONFIG_INCLUDE_DIRS} + /usr/X11/include + ) + + find_library(FONTCONFIG_LIBRARIES NAMES fontconfig + PATHS + ${PC_FONTCONFIG_LIBDIR} + ${PC_FONTCONFIG_LIBRARY_DIRS} + ) + + include(FindPackageHandleStandardArgs) + FIND_PACKAGE_HANDLE_STANDARD_ARGS(Fontconfig DEFAULT_MSG FONTCONFIG_LIBRARIES FONTCONFIG_INCLUDE_DIR ) + + mark_as_advanced(FONTCONFIG_LIBRARIES FONTCONFIG_INCLUDE_DIR) + +endif (FONTCONFIG_LIBRARIES AND FONTCONFIG_INCLUDE_DIR) From 872ed69519fe36add89462de032d8fd873e7b86d Mon Sep 17 00:00:00 2001 From: King_DuckZ Date: Thu, 9 Nov 2017 23:35:28 +0000 Subject: [PATCH 003/149] Whitespace changes --- CMakeLists.txt | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d71d267b..29627f96 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -373,17 +373,17 @@ if(NANA_CMAKE_INSTALL_INCLUDES) endif(NANA_CMAKE_INSTALL_INCLUDES) target_compile_features(${PROJECT_NAME} - PUBLIC cxx_nullptr - PUBLIC cxx_range_for - PUBLIC cxx_lambdas - PUBLIC cxx_decltype_auto - PUBLIC cxx_defaulted_functions - PUBLIC cxx_deleted_functions - PUBLIC cxx_auto_type - PUBLIC cxx_decltype_incomplete_return_types - PUBLIC cxx_defaulted_move_initializers - PUBLIC cxx_noexcept - PUBLIC cxx_rvalue_references + PUBLIC cxx_nullptr + PUBLIC cxx_range_for + PUBLIC cxx_lambdas + PUBLIC cxx_decltype_auto + PUBLIC cxx_defaulted_functions + PUBLIC cxx_deleted_functions + PUBLIC cxx_auto_type + PUBLIC cxx_decltype_incomplete_return_types + PUBLIC cxx_defaulted_move_initializers + PUBLIC cxx_noexcept + PUBLIC cxx_rvalue_references ) # Just for information: From bfba8fa233e458590b36940d9f5cf304ac56b50d Mon Sep 17 00:00:00 2001 From: King_DuckZ Date: Thu, 9 Nov 2017 23:37:17 +0000 Subject: [PATCH 004/149] Fix typo --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 29627f96..7e2d90d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -254,7 +254,7 @@ if(NANA_CMAKE_ENABLE_JPEG) if (JPEG_FOUND) target_include_directories(${PROJECT_NAME PRIVATE ${JPEG_INCLUDE_DIR}) list(APPEND NANA_LINKS ${JPEG_LIBRARIES}) - target_include_directories(${PROJECT_NAME} PRIVATE ${JPEG_LIBRARIES}) + target_link_libraries(${PROJECT_NAME} PRIVATE ${JPEG_LIBRARIES}) target_compile_definitions(${PROJECT_NAME} PUBLIC USE_LIBJPEG_FROM_OS) endif(JPEG_FOUND) else() From 7f35b240b56862b2784520305b4749539557fc68 Mon Sep 17 00:00:00 2001 From: King_DuckZ Date: Thu, 9 Nov 2017 23:37:55 +0000 Subject: [PATCH 005/149] Remove the NANA_LINKS variable since it's not tracking what's really happening anymore. --- CMakeLists.txt | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7e2d90d4..c0b48a52 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -148,7 +148,6 @@ endif(WIN32) if(APPLE) add_definitions(-DAPPLE) include_directories(/opt/X11/include/) - list(APPEND NANA_LINKS -L/opt/X11/lib/ -liconv) target_link_libraries(${PROJECT_NAME} PRIVATE iconv) set(ENABLE_AUDIO OFF) elseif(UNIX) @@ -158,7 +157,6 @@ endif(APPLE) if(UNIX) find_package(X11 REQUIRED) - list(APPEND NANA_LINKS ${X11_LIBRARIES}) target_link_libraries(${PROJECT_NAME} PRIVATE ${X11_LIBRARIES} PRIVATE ${X11_Xft_LIB} @@ -174,7 +172,6 @@ if(UNIX) PRIVATE ${FREETYPE_INCLUDE_DIRS} PRIVATE ${FONTCONFIG_INCLUDE_DIR} ) - list(APPEND NANA_LINKS ${FREETYPE_LIBRARIES}) target_link_libraries(${PROJECT_NAME} PRIVATE ${FREETYPE_LIBRARIES} PRIVATE ${FONTCONFIG_LIBRARIES} @@ -208,10 +205,8 @@ if(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") # AN target_link_libraries(${PROJECT_NAME} PRIVATE Threads::Threads) if(NANA_CMAKE_SHARED_LIB) - list(APPEND NANA_LINKS -lgcc -lstdc++) else() set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++") - # message("Setting NANA_LINKS to -static-libgcc -static-libstdc++ -pthread or ${NANA_LINKS}") endif(NANA_CMAKE_SHARED_LIB) if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.3) @@ -237,7 +232,6 @@ if(NANA_CMAKE_ENABLE_PNG) if(NANA_CMAKE_LIBPNG_FROM_OS) find_package(PNG) if (PNG_FOUND) - list(APPEND NANA_LINKS ${PNG_LIBRARIES}) target_include_directories(${PROJECT_NAME} PRIVATE PNG::PNG) target_compile_definitions(${PROJECT_NAME} PUBLIC USE_LIBPNG_FROM_OS) endif(PNG_FOUND) @@ -253,7 +247,6 @@ if(NANA_CMAKE_ENABLE_JPEG) find_package(JPEG) if (JPEG_FOUND) target_include_directories(${PROJECT_NAME PRIVATE ${JPEG_INCLUDE_DIR}) - list(APPEND NANA_LINKS ${JPEG_LIBRARIES}) target_link_libraries(${PROJECT_NAME} PRIVATE ${JPEG_LIBRARIES}) target_compile_definitions(${PROJECT_NAME} PUBLIC USE_LIBJPEG_FROM_OS) endif(JPEG_FOUND) @@ -269,7 +262,6 @@ if(NANA_CMAKE_ENABLE_AUDIO) find_package(ASOUND) if (ASOUND_FOUND) target_include_directories(${PROJECT_NAME PRIVATE ${ASOUND_INCLUDE_DIRS}) - list(APPEND NANA_LINKS -lasound) target_link_libraries(${PROJECT_NAME} PRIVATE ${ASOUND_LIBRARIES}) else(ASOUND_FOUND) message(FATAL_ERROR "libasound is not found") @@ -293,7 +285,6 @@ elseif (NANA_CMAKE_FIND_BOOST_FILESYSTEM OR NANA_CMAKE_BOOST_FILESYSTEM_FORCE) find_package(Boost COMPONENTS filesystem) if (Boost_FOUND) add_definitions(-DNANA_BOOST_FILESYSTEM_AVAILABLE) - list(APPEND NANA_LINKS ${Boost_LIBRARIES}) ###### FIRST !!!!!!!!!!!!!!!!! add is not first target_link_libraries(${PROJECT_NAME} PUBLIC Boost::Boost) endif (Boost_FOUND) set(Boost_USE_STATIC_LIBS ON) @@ -394,7 +385,6 @@ 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}) From b5c69dc3867be16f3d40ecbd70d0075adc854c72 Mon Sep 17 00:00:00 2001 From: King_DuckZ Date: Thu, 9 Nov 2017 23:53:32 +0000 Subject: [PATCH 006/149] Use target_compile_definitions instead of add_definitions. This allows macro definitions to be inherited. There might be symbols that can be made private. This is likely breaking the build on non-linux systems. --- CMakeLists.txt | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c0b48a52..5bac756c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -96,7 +96,7 @@ add_library(${PROJECT_NAME} ${sources} ) ########### OS if(WIN32) - add_definitions(-DWIN32) + target_compile_definitions(${PROJECT_NAME} PUBLIC WIN32) set(CMAKE_DEBUG_POSTFIX "_d") #Global MSVC definitions. You may prefer the hand-tuned sln and projects from the nana repository. if(MSVC) @@ -123,8 +123,10 @@ if(WIN32) 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) + target_compile_definitions(${PROJECT_NAME} + PUBLIC STD_THREAD_NOT_SUPPORTED + PUBLIC NANA_ENABLE_MINGW_STD_THREADS_WITH_MEGANZ + ) endif(NANA_CMAKE_ENABLE_MINGW_STD_THREADS_WITH_MEGANZ) endif(MINGW) @@ -146,12 +148,12 @@ endif(WIN32) if(APPLE) - add_definitions(-DAPPLE) + target_compile_definitions(${PROJECT_NAME} PUBLIC APPLE) include_directories(/opt/X11/include/) target_link_libraries(${PROJECT_NAME} PRIVATE iconv) set(ENABLE_AUDIO OFF) elseif(UNIX) - add_definitions(-Dlinux) + target_compile_definitions(${PROJECT_NAME} PUBLIC linux) message("added -D linux") endif(APPLE) @@ -236,13 +238,13 @@ if(NANA_CMAKE_ENABLE_PNG) target_compile_definitions(${PROJECT_NAME} PUBLIC USE_LIBPNG_FROM_OS) endif(PNG_FOUND) else() - add_definitions(-DNANA_ENABLE_PNG) + target_compile_definitions(${PROJECT_NAME} PUBLIC NANA_ENABLE_PNG) endif(NANA_CMAKE_LIBPNG_FROM_OS) endif(NANA_CMAKE_ENABLE_PNG) # Find JPEG if(NANA_CMAKE_ENABLE_JPEG) - add_definitions(-DNANA_ENABLE_JPEG) + target_compile_definitions(${PROJECT_NAME} PUBLIC NANA_ENABLE_JPEG) if(NANA_CMAKE_LIBJPEG_FROM_OS) find_package(JPEG) if (JPEG_FOUND) @@ -257,7 +259,7 @@ endif(NANA_CMAKE_ENABLE_JPEG) # Find ASOUND if(NANA_CMAKE_ENABLE_AUDIO) - add_definitions(-DNANA_ENABLE_AUDIO) + target_compile_definitions(${PROJECT_NAME} PUBLIC NANA_ENABLE_AUDIO) if(UNIX) find_package(ASOUND) if (ASOUND_FOUND) @@ -271,12 +273,12 @@ endif(NANA_CMAKE_ENABLE_AUDIO) # Find/Select filesystem if (NANA_CMAKE_NANA_FILESYSTEM_FORCE) - add_definitions(-DNANA_FILESYSTEM_FORCE) + target_compile_definitions(${PROJECT_NAME} PUBLIC NANA_FILESYSTEM_FORCE) elseif (NANA_CMAKE_STD_FILESYSTEM_FORCE) - add_definitions(-DSTD_FILESYSTEM_FORCE) + target_compile_definitions(${PROJECT_NAME} PUBLIC STD_FILESYSTEM_FORCE) elseif (NANA_CMAKE_FIND_BOOST_FILESYSTEM OR NANA_CMAKE_BOOST_FILESYSTEM_FORCE) if (NANA_CMAKE_BOOST_FILESYSTEM_FORCE) - add_definitions(-DNANA_BOOST_FILESYSTEM_FORCE) + target_compile_definitions(${PROJECT_NAME} PUBLIC NANA_BOOST_FILESYSTEM_FORCE) endif(NANA_CMAKE_BOOST_FILESYSTEM_FORCE) # https://cmake.org/cmake/help/git-master/module/FindBoost.html # Implicit dependencies such as Boost::filesystem requiring Boost::system will be automatically detected and satisfied, @@ -284,7 +286,7 @@ elseif (NANA_CMAKE_FIND_BOOST_FILESYSTEM OR NANA_CMAKE_BOOST_FILESYSTEM_FORCE) # If using Boost::thread, then Thread::Thread will also be added automatically. find_package(Boost COMPONENTS filesystem) if (Boost_FOUND) - add_definitions(-DNANA_BOOST_FILESYSTEM_AVAILABLE) + target_compile_definitions(${PROJECT_NAME} PUBLIC NANA_BOOST_FILESYSTEM_AVAILABLE) target_link_libraries(${PROJECT_NAME} PUBLIC Boost::Boost) endif (Boost_FOUND) set(Boost_USE_STATIC_LIBS ON) @@ -294,13 +296,13 @@ endif (NANA_CMAKE_NANA_FILESYSTEM_FORCE) ######## Nana options -add_definitions(-DNANA_IGNORE_CONF) +target_compile_definitions(${PROJECT_NAME} PUBLIC NANA_IGNORE_CONF) if(NANA_CMAKE_VERBOSE_PREPROCESSOR) - add_definitions(-DVERBOSE_PREPROCESSOR) + target_compile_definitions(${PROJECT_NAME} PUBLIC VERBOSE_PREPROCESSOR) endif(NANA_CMAKE_VERBOSE_PREPROCESSOR) if(NANA_CMAKE_AUTOMATIC_GUI_TESTING) - add_definitions(-DNANA_AUTOMATIC_GUI_TESTING) - enable_testing () + target_compile_definitions(${PROJECT_NAME} PRIVATE NANA_AUTOMATIC_GUI_TESTING) + enable_testing () endif(NANA_CMAKE_AUTOMATIC_GUI_TESTING) From d3f55776256de89965dc9488cf4d4f1e8076ac3e Mon Sep 17 00:00:00 2001 From: King_DuckZ Date: Thu, 9 Nov 2017 23:53:55 +0000 Subject: [PATCH 007/149] These need be set *before* find_package, or it will be too late. --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5bac756c..d25dcb10 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -284,13 +284,13 @@ elseif (NANA_CMAKE_FIND_BOOST_FILESYSTEM OR NANA_CMAKE_BOOST_FILESYSTEM_FORCE) # 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. + set(Boost_USE_STATIC_LIBS ON) + set(Boost_USE_STATIC_RUNTIME ON) # ?? find_package(Boost COMPONENTS filesystem) if (Boost_FOUND) target_compile_definitions(${PROJECT_NAME} PUBLIC NANA_BOOST_FILESYSTEM_AVAILABLE) target_link_libraries(${PROJECT_NAME} PUBLIC Boost::Boost) endif (Boost_FOUND) - set(Boost_USE_STATIC_LIBS ON) - set(Boost_USE_STATIC_RUNTIME ON) # ?? endif (NANA_CMAKE_NANA_FILESYSTEM_FORCE) From 309a95d32195fe6d8bba655d1189cf84b047ed1b Mon Sep 17 00:00:00 2001 From: King_DuckZ Date: Thu, 9 Nov 2017 23:54:53 +0000 Subject: [PATCH 008/149] Maybe not needed anymore? --- CMakeLists.txt | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d25dcb10..106a0bde 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,16 +55,6 @@ option(NANA_CMAKE_NANA_FILESYSTEM_FORCE "Force nana filesystem over ISO and boos option(NANA_CMAKE_STD_FILESYSTEM_FORCE "Use of STD filesystem?(a compilation error will ocurre if not available)" OFF) option(NANA_CMAKE_BOOST_FILESYSTEM_FORCE "Force use of Boost filesystem if available (over STD)?" OFF) -########### Compatibility with CMake 3.1 -if(POLICY CMP0054) - # http://www.cmake.org/cmake/help/v3.1/policy/CMP0054.html - cmake_policy(SET CMP0054 OLD) -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(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 From 32e6ee532c7829b01446ff817d83e631bb5efbbd Mon Sep 17 00:00:00 2001 From: King_DuckZ Date: Fri, 10 Nov 2017 00:16:50 +0000 Subject: [PATCH 009/149] Fix build after rebase. --- CMakeLists.txt | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 106a0bde..7090e19a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,7 +82,11 @@ foreach(subdir ${NANA_SOURCE_SUBDIRS}) # message("Subir: ${subdir}") # message("Files: ${sources}") endforeach(subdir ${NANA_SOURCE_SUBDIRS}) -add_library(${PROJECT_NAME} ${sources} ) +if(NANA_CMAKE_SHARED_LIB) + add_library(${PROJECT_NAME} SHARED ${sources} ) +else() + add_library(${PROJECT_NAME} STATIC ${sources} ) +endif(NANA_CMAKE_SHARED_LIB) ########### OS if(WIN32) @@ -196,8 +200,7 @@ if(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") # AN find_package(Threads REQUIRED) target_link_libraries(${PROJECT_NAME} PRIVATE Threads::Threads) - if(NANA_CMAKE_SHARED_LIB) - else() + if(NOT NANA_CMAKE_SHARED_LIB) set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++") endif(NANA_CMAKE_SHARED_LIB) @@ -302,12 +305,6 @@ if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") add_definitions(-fmax-errors=3) endif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") -if(NANA_CMAKE_SHARED_LIB) - add_library(${PROJECT_NAME} SHARED ${sources} ) -else() - add_library(${PROJECT_NAME} STATIC ${sources} ) -endif(NANA_CMAKE_SHARED_LIB) - target_include_directories(${PROJECT_NAME} PUBLIC ${NANA_INCLUDE_DIR}) # Headers: use INCLUDE_DIRECTORIES From b9946f262403c6218f8edf7d3f5a9ddc994a2b88 Mon Sep 17 00:00:00 2001 From: King_DuckZ Date: Fri, 10 Nov 2017 00:29:07 +0000 Subject: [PATCH 010/149] Fix up some wrong parts I missed to conform with the rest of my changes. --- CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7090e19a..9af2d3a8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -227,7 +227,7 @@ if(NANA_CMAKE_ENABLE_PNG) if(NANA_CMAKE_LIBPNG_FROM_OS) find_package(PNG) if (PNG_FOUND) - target_include_directories(${PROJECT_NAME} PRIVATE PNG::PNG) + target_include_directories(${PROJECT_NAME} SYSTEM PRIVATE PNG::PNG) target_compile_definitions(${PROJECT_NAME} PUBLIC USE_LIBPNG_FROM_OS) endif(PNG_FOUND) else() @@ -241,12 +241,12 @@ if(NANA_CMAKE_ENABLE_JPEG) if(NANA_CMAKE_LIBJPEG_FROM_OS) find_package(JPEG) if (JPEG_FOUND) - target_include_directories(${PROJECT_NAME PRIVATE ${JPEG_INCLUDE_DIR}) + target_include_directories(${PROJECT_NAME SYSTEM PRIVATE ${JPEG_INCLUDE_DIR}) target_link_libraries(${PROJECT_NAME} PRIVATE ${JPEG_LIBRARIES}) target_compile_definitions(${PROJECT_NAME} PUBLIC USE_LIBJPEG_FROM_OS) endif(JPEG_FOUND) else() - add_definitions(-DNANA_ENABLE_JPEG) + target_compile_definitions(${PROJECT_NAME} PUBLIC NANA_ENABLE_JPEG) endif(NANA_CMAKE_LIBJPEG_FROM_OS) endif(NANA_CMAKE_ENABLE_JPEG) From 97297ae511bd7f26bcee334aad5143017eb5af05 Mon Sep 17 00:00:00 2001 From: King_DuckZ Date: Fri, 10 Nov 2017 00:46:08 +0000 Subject: [PATCH 011/149] Fix cmake warning. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9af2d3a8..06b66eaf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -202,7 +202,7 @@ if(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") # AN if(NOT NANA_CMAKE_SHARED_LIB) set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++") - endif(NANA_CMAKE_SHARED_LIB) + endif(NOT NANA_CMAKE_SHARED_LIB) if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.3) # IS_GNUCXX < 5.3 From b713b01dbe8489787842a1f306b0eedb29dc2be5 Mon Sep 17 00:00:00 2001 From: King_DuckZ Date: Fri, 10 Nov 2017 00:49:46 +0000 Subject: [PATCH 012/149] Fix missing closing brace. --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 06b66eaf..64bb1bd6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -241,7 +241,7 @@ if(NANA_CMAKE_ENABLE_JPEG) if(NANA_CMAKE_LIBJPEG_FROM_OS) find_package(JPEG) if (JPEG_FOUND) - target_include_directories(${PROJECT_NAME SYSTEM PRIVATE ${JPEG_INCLUDE_DIR}) + target_include_directories(${PROJECT_NAME} SYSTEM PRIVATE ${JPEG_INCLUDE_DIR}) target_link_libraries(${PROJECT_NAME} PRIVATE ${JPEG_LIBRARIES}) target_compile_definitions(${PROJECT_NAME} PUBLIC USE_LIBJPEG_FROM_OS) endif(JPEG_FOUND) @@ -256,7 +256,7 @@ if(NANA_CMAKE_ENABLE_AUDIO) if(UNIX) find_package(ASOUND) if (ASOUND_FOUND) - target_include_directories(${PROJECT_NAME PRIVATE ${ASOUND_INCLUDE_DIRS}) + target_include_directories(${PROJECT_NAME} PRIVATE ${ASOUND_INCLUDE_DIRS}) target_link_libraries(${PROJECT_NAME} PRIVATE ${ASOUND_LIBRARIES}) else(ASOUND_FOUND) message(FATAL_ERROR "libasound is not found") From 1f53aa334440b2f659ce471808aea02035bbcf55 Mon Sep 17 00:00:00 2001 From: qPCR4vir Date: Sat, 14 Jul 2018 02:06:10 +0200 Subject: [PATCH 013/149] nana hotfix point to nana-demo hotfix --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 42f63050..36f8353e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,7 +41,7 @@ matrix: - llvm-toolchain-precise before_install: - - git clone --depth=1 --branch=develop https://github.com/qPCR4vir/nana-demo.git ../nana-demo + - git clone --depth=1 --branch=hotfix 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 From 74cc3ce475b7e8b953e271ea227667d689343180 Mon Sep 17 00:00:00 2001 From: qPCR4vir Date: Thu, 16 Aug 2018 08:48:32 +0200 Subject: [PATCH 014/149] VERSION --- CMakeLists.txt | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1d023469..033af9ea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,29 +1,33 @@ # CMake configuration for Nana -# Author: Andrew Kornilov(https://github.com/ierofant) # Contributors: +# Andrew Kornilov (ierofant) - original version # Jinhao -# Robert Hauck - Enable support for PNG/Freetype -# Qiangqiang Wu - Add biicode support # Ariel Vina-Rodriguez (qPCR4vir) +# Robert Hauck - Enable support for PNG/Freetype # Pavel O. - fix compilation with boost::filesystem (#281) # Frostbane - Add option for compiling a shared library (#263,#265) +# Qiangqiang Wu - Add biicode support # # Nana uses some build systems: MS-VS solution, MAKE, bakefile, codeblock, etc. manually optimized. -# In the future CMake could be the prefered, and maybe will be used to generate the others and the central nana repo -# will distribute all of them. But by now CMake is just one of them and all the other distributed build system -# files/projects are manually write. This current CMakeList.txt reflect this fact and that is why we don't +# Maybe CMake will be used in the future to generate some of them in the central nana repository. +# But by now CMake is just one option and all the other distributed build system +# files/projects are manually writen. This current CMakeList.txt reflect this fact and that is why we don't # generate here configurated *.h files or explicitly enumerate the sources files: anyway this CM-list # will be "touched" to force a re-run of cmake. -#https://cmake.org/cmake-tutorial/ -#https://cmake.org/cmake/help/v3.3/module/CMakeDependentOption.html?highlight=cmakedependentoption +# https://cliutils.gitlab.io/modern-cmake/ +# https://cmake.org/cmake-tutorial/ +# https://cmake.org/cmake/help/v3.3/module/CMakeDependentOption.html?highlight=cmakedependentoption # use CACHE FORCE or set(ENABLE_MINGW_STD_THREADS_WITH_MEGANZ ON) or delete CMakecache.txt or the entirely build dir # if your changes don't execute # It seems that project() defines essential system variables like CMAKE_FIND_LIBRARY_PREFIXES. # https://bbs.archlinux.org/viewtopic.php?id=84967 -project(nana) -cmake_minimum_required(VERSION 2.8) +cmake_minimum_required(VERSION 3.12) +project(nana VERSION 1.6.1 + DESCRIPTION "C++ GUI library" + HOMEPAGE_URL http://nanapro.org + LANGUAGES CXX ) option(NANA_CMAKE_INSTALL_INCLUDES "Install nana includes when compile the library" ON) option(NANA_CMAKE_ENABLE_MINGW_STD_THREADS_WITH_MEGANZ "replaced boost.thread with meganz's mingw-std-threads." OFF) @@ -302,6 +306,7 @@ else() endif() target_include_directories(${PROJECT_NAME} PUBLIC ${NANA_INCLUDE_DIR}) +target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_14) # target_link_libraries(${PROJECT_NAME} ${NANA_LINKS}) # Headers: use INCLUDE_DIRECTORIES From 7a2b97813a0d53ed5b68a5a410fd051343e5d486 Mon Sep 17 00:00:00 2001 From: qPCR4vir Date: Thu, 16 Aug 2018 08:49:55 +0200 Subject: [PATCH 015/149] CLion install works now --- CMakeLists.txt | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 033af9ea..c18479ad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,7 +40,6 @@ option(NANA_CMAKE_SHARED_LIB "Compile nana as a shared library." OFF) option(NANA_CMAKE_VERBOSE_PREPROCESSOR "Show annoying debug messages during compilation." ON) option(NANA_CMAKE_STOP_VERBOSE_PREPROCESSOR "Stop compilation after showing the annoying debug messages." OFF) option(NANA_CMAKE_AUTOMATIC_GUI_TESTING "Activate automatic GUI testing?" OFF) -option(NANA_CLION "Activate some CLion specific workarounds" OFF) # The ISO C++ File System Technical Specification (ISO-TS, or STD) is optional. # http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4100.pdf @@ -135,13 +134,11 @@ endif() # Using gcc: gcc 4.8 don't support C++14 and make_unique. You may want to update at least to 4.9. # gcc 5.3 and 5.4 include filesytem, but you need to add the link flag: -lstdc++fs # -# In Windows, the gcc which come with CLion was 4.8 from MinGW. -# CLion was updated to MinGW with gcc 6.3 ? Allways check this in File/Settings.../toolchains +# In Windows, with CLion Allways check in File/Settings.../toolchains # You could install MinGW-w64 from the TDM-GCC Compiler Suite for Windows which will update you to gcc 5.1. # It is posible to follow https://computingabdn.com/softech/mingw-howto-install-gcc-for-windows/ # and install MinGW with gcc 7.1 with has STD_THREADS and fs, from: https://sourceforge.net/projects/mingw-w64/files/ # -# # see at end of: https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_dynamic_or_shared.html # if(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") @@ -320,14 +317,6 @@ 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}") -endif() - if(NANA_CMAKE_SHARED_LIB) if(WIN32) set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) From 95b386051c7a722e0b3dd56fb280ed77731b345b Mon Sep 17 00:00:00 2001 From: qPCR4vir Date: Thu, 16 Aug 2018 20:35:03 +0200 Subject: [PATCH 016/149] travis cmake 3.12 update ubuntu install --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 36f8353e..79e65776 100644 --- a/.travis.yml +++ b/.travis.yml @@ -44,7 +44,7 @@ before_install: - git clone --depth=1 --branch=hotfix 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 + - wget --no-check-certificate --no-clobber -O /tmp/tools/cmake https://cmake.org/files/v3.12/cmake-3.12.0-rc3-Linux-x86_64.sh || true - chmod -R +x /tmp/tools install: From 3dbbd175fab6b93d4638d07b658d67124850727e Mon Sep 17 00:00:00 2001 From: qPCR4vir Date: Thu, 16 Aug 2018 20:37:16 +0200 Subject: [PATCH 017/149] git clone nana-demo branch=cmake-dev --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 79e65776..0f761bc9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,7 +41,7 @@ matrix: - llvm-toolchain-precise before_install: - - git clone --depth=1 --branch=hotfix https://github.com/qPCR4vir/nana-demo.git ../nana-demo + - git clone --depth=1 --branch=cmake-dev https://github.com/qPCR4vir/nana-demo.git ../nana-demo - export PATH="$HOME/bin:$PATH" #- mkdir ~/bin #it seemd that a bin already exists from 20170901 - wget --no-check-certificate --no-clobber -O /tmp/tools/cmake https://cmake.org/files/v3.12/cmake-3.12.0-rc3-Linux-x86_64.sh || true From f789d808be25f433353a11eb1fa43d68be663b40 Mon Sep 17 00:00:00 2001 From: qPCR4vir Date: Sun, 19 Aug 2018 20:49:17 +0200 Subject: [PATCH 018/149] revise --- .travis.yml | 1 - CMakeLists.txt | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 0f761bc9..69624c16 100644 --- a/.travis.yml +++ b/.travis.yml @@ -43,7 +43,6 @@ matrix: before_install: - git clone --depth=1 --branch=cmake-dev https://github.com/qPCR4vir/nana-demo.git ../nana-demo - export PATH="$HOME/bin:$PATH" - #- mkdir ~/bin #it seemd that a bin already exists from 20170901 - wget --no-check-certificate --no-clobber -O /tmp/tools/cmake https://cmake.org/files/v3.12/cmake-3.12.0-rc3-Linux-x86_64.sh || true - chmod -R +x /tmp/tools diff --git a/CMakeLists.txt b/CMakeLists.txt index c18479ad..f65bd8d8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -367,6 +367,7 @@ message ( "NANA_CLION = " ${NANA_CLION}) message ( "CMAKE_MAKE_PROGRAM = " ${CMAKE_MAKE_PROGRAM}) message ( "CMAKE_CXX_COMPILER_VERSION = " ${CMAKE_CXX_COMPILER_VERSION}) +message ( "NANA_CMAKE_NANA_FILESYSTEM_FORCE = " ${NANA_CMAKE_NANA_FILESYSTEM_FORCE}) 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}) From 676754904252eab16e14a1fdb706e35069e7978b Mon Sep 17 00:00:00 2001 From: qPCR4vir Date: Tue, 21 Aug 2018 16:44:54 +0200 Subject: [PATCH 019/149] target centric cmake --- CMakeLists.txt | 93 ++++++++++++++++++++++++++------------------------ 1 file changed, 48 insertions(+), 45 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 70310509..a31300de 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,6 +29,54 @@ project(nana VERSION 1.6.1 HOMEPAGE_URL http://nanapro.org LANGUAGES CXX ) + + +####################### Main setting of Nana sources, targets and install + +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 + ) +if(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) +endforeach() + +if("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") + add_definitions(-fmax-errors=3) +endif() + +set(CMAKE_DEBUG_POSTFIX "_d") + +if(NANA_CMAKE_SHARED_LIB) + add_library(${PROJECT_NAME} SHARED ${SOURCES}) +else() + add_library(${PROJECT_NAME} STATIC ${SOURCES}) +endif() + +target_include_directories(${PROJECT_NAME} PUBLIC ${NANA_INCLUDE_DIR}) +target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_14) # +target_link_libraries(${PROJECT_NAME} ${NANA_LINKS}) + + option(NANA_CMAKE_INSTALL_INCLUDES "Install nana includes when compile the library" ON) option(NANA_CMAKE_ENABLE_MINGW_STD_THREADS_WITH_MEGANZ "replaced boost.thread with meganz's mingw-std-threads." OFF) option(NANA_CMAKE_ENABLE_PNG "Enable the use of PNG" OFF) @@ -261,51 +309,6 @@ if(NANA_CMAKE_AUTOMATIC_GUI_TESTING) endif() -####################### Main setting of Nana sources, targets and install - -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 - ) -if(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) -endforeach() - -if("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") - add_definitions(-fmax-errors=3) -endif() - -set(CMAKE_DEBUG_POSTFIX "_d") - -if(NANA_CMAKE_SHARED_LIB) - add_library(${PROJECT_NAME} SHARED ${SOURCES}) -else() - add_library(${PROJECT_NAME} STATIC ${SOURCES}) -endif() - -target_include_directories(${PROJECT_NAME} PUBLIC ${NANA_INCLUDE_DIR}) -target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_14) # -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) From ff2444b3cba40173a0bf7b5f04e4264da57477de Mon Sep 17 00:00:00 2001 From: qPCR4vir Date: Tue, 21 Aug 2018 16:52:30 +0200 Subject: [PATCH 020/149] use BUILD_SHARED_LIBS and target_sources() --- CMakeLists.txt | 72 ++++++++++++++++++++++---------------------------- 1 file changed, 32 insertions(+), 40 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a31300de..5f8b7b50 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,18 +25,25 @@ cmake_minimum_required(VERSION 3.12) project(nana VERSION 1.6.1 - DESCRIPTION "C++ GUI library" - HOMEPAGE_URL http://nanapro.org - LANGUAGES CXX ) + DESCRIPTION "C++ GUI library" + HOMEPAGE_URL http://nanapro.org + LANGUAGES CXX ) -####################### Main setting of Nana sources, targets and install +option(BUILD_SHARED_LIBS "Compile nana as a shared library." OFF) + +####################### Main setting of Nana targets, sources and installs + + +add_library(${PROJECT_NAME} ) + 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 /. +set(NANA_SOURCE_SUBDIRS + /. /detail /filesystem /gui @@ -49,7 +56,8 @@ set(NANA_SOURCE_SUBDIRS /. /threads ) if(NANA_CMAKE_ENABLE_AUDIO) - list(APPEND NANA_SOURCE_SUBDIRS /audio + list(APPEND NANA_SOURCE_SUBDIRS + /audio /audio/detail ) endif() @@ -60,18 +68,7 @@ foreach(subdir ${NANA_SOURCE_SUBDIRS}) aux_source_directory(${NANA_SOURCE_DIR}${subdir} SOURCES) endforeach() -if("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") - add_definitions(-fmax-errors=3) -endif() - -set(CMAKE_DEBUG_POSTFIX "_d") - -if(NANA_CMAKE_SHARED_LIB) - add_library(${PROJECT_NAME} SHARED ${SOURCES}) -else() - add_library(${PROJECT_NAME} STATIC ${SOURCES}) -endif() - +target_sources(${PROJECT_NAME} PRIVATE ${SOURCES}) target_include_directories(${PROJECT_NAME} PUBLIC ${NANA_INCLUDE_DIR}) target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_14) # target_link_libraries(${PROJECT_NAME} ${NANA_LINKS}) @@ -84,7 +81,6 @@ option(NANA_CMAKE_LIBPNG_FROM_OS "Use libpng from operating system." ON) option(NANA_CMAKE_ENABLE_JPEG "Enable the use of JPEG" OFF) option(NANA_CMAKE_LIBJPEG_FROM_OS "Use libjpeg from operating system." ON) option(NANA_CMAKE_ENABLE_AUDIO "Enable class audio::play for PCM playback." OFF) -option(NANA_CMAKE_SHARED_LIB "Compile nana as a shared library." OFF) option(NANA_CMAKE_VERBOSE_PREPROCESSOR "Show annoying debug messages during compilation." ON) option(NANA_CMAKE_STOP_VERBOSE_PREPROCESSOR "Stop compilation after showing the annoying debug messages." OFF) option(NANA_CMAKE_AUTOMATIC_GUI_TESTING "Activate automatic GUI testing?" OFF) @@ -129,10 +125,10 @@ if(WIN32) if(MSVC_USE_STATIC_RUNTIME) foreach(flag - CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE - CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO - CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE - CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) + CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE + CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO + CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) if(${flag} MATCHES "/MD") string(REGEX REPLACE "/MD" "/MT" ${flag} "${${flag}}") endif() @@ -191,11 +187,11 @@ endif() # if(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") - if("${CMAKE_SYSTEM_NAME}" MATCHES "FreeBSD") + if("${CMAKE_SYSTEM_NAME}" MATCHES "FreeBSD") set(CMAKE_CXX_FLAGS "-std=gnu++14 -Wall -I/usr/local/include") - else() - set(CMAKE_CXX_FLAGS "-std=gnu++14 -Wall") - endif() + else() + set(CMAKE_CXX_FLAGS "-std=gnu++14 -Wall") + endif() else() set(CMAKE_CXX_FLAGS "-std=c++14 -Wall") endif() @@ -224,7 +220,7 @@ endif() if (APPLE AND "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") # APPLE Clang - list(APPEND NANA_LINKS -stdlib=libstdc++) + list(APPEND NANA_LINKS -stdlib=libstdc++) endif () @@ -297,20 +293,11 @@ elseif(NANA_CMAKE_FIND_BOOST_FILESYSTEM OR NANA_CMAKE_BOOST_FILESYSTEM_FORCE) endif() -######## Nana options - -add_definitions(-DNANA_IGNORE_CONF) -if(NANA_CMAKE_VERBOSE_PREPROCESSOR) - add_definitions(-DVERBOSE_PREPROCESSOR) -endif() -if(NANA_CMAKE_AUTOMATIC_GUI_TESTING) - add_definitions(-DNANA_AUTOMATIC_GUI_TESTING) - enable_testing() +if("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") + add_definitions(-fmax-errors=3) endif() - - # Headers: use INCLUDE_DIRECTORIES - # Libraries: use FIND_LIBRARY and link with the result of it (try to avoid LINK_DIRECTORIES) +set(CMAKE_DEBUG_POSTFIX "_d") # Installing: the static "nana lib" will be in DESTDIR/CMAKE_INSTALL_PREFIX/lib/ # and the includes files "include/nana/" in DESTDIR/CMAKE_INSTALL_PREFIX/include/nana/ @@ -352,7 +339,12 @@ endif() # Just for information: +include(CMakePrintHelpers) + message ("") +cmake_print_variables(SOURCES) + + 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}) From f24e60425b8eafea307ef4d57a41e38db9428e57 Mon Sep 17 00:00:00 2001 From: qPCR4vir Date: Tue, 21 Aug 2018 16:54:56 +0200 Subject: [PATCH 021/149] use CMakePrintHelpers, move installs --- CMakeLists.txt | 97 ++++++++++++++++++++++++++++---------------------- 1 file changed, 54 insertions(+), 43 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5f8b7b50..f930bae4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,6 +73,60 @@ target_include_directories(${PROJECT_NAME} PUBLIC ${NANA_INCLUDE_DIR}) target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_14) # 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) + +# Installing: the static "nana lib" will be in DESTDIR/CMAKE_INSTALL_PREFIX/lib/ +# and the includes files "include/nana/" in DESTDIR/CMAKE_INSTALL_PREFIX/include/nana/ +# unfortunatelly install() is still ignored by CLion: +# https://intellij-support.jetbrains.com/hc/en-us/community/posts/205822949-CMake-install-isn-t-supported- +install(TARGETS ${PROJECT_NAME} ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin) + +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}" + LINK_FLAGS "${CMAKE_SHARED_LINKER_FLAGS_INIT} -Wl,--output-def=${CMAKE_CURRENT_BINARY_DIR}/lib${PROJECT_NAME}.def") + + add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + COMMAND echo " Generating import library" + COMMAND "${DLLTOOL}" --dllname "lib${PROJECT_NAME}.dll" + --input-def "lib${PROJECT_NAME}.def" + --output-lib "lib${PROJECT_NAME}.lib") + + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/lib${PROJECT_NAME}.def" + "${CMAKE_CURRENT_BINARY_DIR}/lib${PROJECT_NAME}.lib" DESTINATION lib) + endif() + endif() +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) + message("The Nana include files will be installed in ${CMAKE_INSTALL_PREFIX}/include") +endif() + + +######## Nana options + +add_definitions(-DNANA_IGNORE_CONF) +if(NANA_CMAKE_VERBOSE_PREPROCESSOR) + add_definitions(-DVERBOSE_PREPROCESSOR) +endif() +if(NANA_CMAKE_AUTOMATIC_GUI_TESTING) + add_definitions(-DNANA_AUTOMATIC_GUI_TESTING) + enable_testing() +endif() + + option(NANA_CMAKE_INSTALL_INCLUDES "Install nana includes when compile the library" ON) option(NANA_CMAKE_ENABLE_MINGW_STD_THREADS_WITH_MEGANZ "replaced boost.thread with meganz's mingw-std-threads." OFF) @@ -103,12 +157,6 @@ option(NANA_CMAKE_NANA_FILESYSTEM_FORCE "Force nana filesystem over ISO and boos option(NANA_CMAKE_STD_FILESYSTEM_FORCE "Use of STD filesystem?(a compilation error will ocurre if not available)" OFF) option(NANA_CMAKE_BOOST_FILESYSTEM_FORCE "Force use of Boost filesystem if available (over STD)?" OFF) -########### Compatibility with CMake 3.1 -if(POLICY CMP0054) - # http://www.cmake.org/cmake/help/v3.1/policy/CMP0054.html - cmake_policy(SET CMP0054 NEW) -endif() - ########### OS if(WIN32) @@ -299,43 +347,6 @@ endif() set(CMAKE_DEBUG_POSTFIX "_d") -# Installing: the static "nana lib" will be in DESTDIR/CMAKE_INSTALL_PREFIX/lib/ -# and the includes files "include/nana/" in DESTDIR/CMAKE_INSTALL_PREFIX/include/nana/ -# unfortunatelly install() is still ignored by CLion: -# https://intellij-support.jetbrains.com/hc/en-us/community/posts/205822949-CMake-install-isn-t-supported- -install(TARGETS ${PROJECT_NAME} ARCHIVE DESTINATION lib - LIBRARY DESTINATION lib - RUNTIME DESTINATION bin) - -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}" - LINK_FLAGS "${CMAKE_SHARED_LINKER_FLAGS_INIT} -Wl,--output-def=${CMAKE_CURRENT_BINARY_DIR}/lib${PROJECT_NAME}.def") - - add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD - WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" - COMMAND echo " Generating import library" - COMMAND "${DLLTOOL}" --dllname "lib${PROJECT_NAME}.dll" - --input-def "lib${PROJECT_NAME}.def" - --output-lib "lib${PROJECT_NAME}.lib") - - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/lib${PROJECT_NAME}.def" - "${CMAKE_CURRENT_BINARY_DIR}/lib${PROJECT_NAME}.lib" DESTINATION lib) - endif() - endif() -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) - message("The Nana include files will be installed in ${CMAKE_INSTALL_PREFIX}/include") -endif() # Just for information: From d27bb0a7b32d7d627e40de8bcb26cd07afcb534f Mon Sep 17 00:00:00 2001 From: qPCR4vir Date: Thu, 23 Aug 2018 15:17:51 +0200 Subject: [PATCH 022/149] try to add headers to projects --- CMakeLists.txt | 84 ++++++++++++++++++++++++++++-------- include/nana/basic_types.hpp | 6 +-- 2 files changed, 69 insertions(+), 21 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f930bae4..2986620b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,18 +30,18 @@ project(nana VERSION 1.6.1 LANGUAGES CXX ) - +# instead of option(NANA_CMAKE_SHARED_LIB "Compile nana as a shared library." OFF) ?? option(BUILD_SHARED_LIBS "Compile nana as a shared library." OFF) +set(NANA_CMAKE_SHARED_LIB ${BUILD_SHARED_LIBS}) + ####################### Main setting of Nana targets, sources and installs - add_library(${PROJECT_NAME} ) - +### collect all source sub-directories in a list to avoid duplication here 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 @@ -61,37 +61,85 @@ if(NANA_CMAKE_ENABLE_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) endforeach() target_sources(${PROJECT_NAME} PRIVATE ${SOURCES}) -target_include_directories(${PROJECT_NAME} PUBLIC ${NANA_INCLUDE_DIR}) + + +### collect all headers sub-directories in a list to avoid duplication here +# To show .h files in Visual Studio, add them to the list of sources in add_executable / add_library / target_sources +# and Use SOURCE_GROUP if all your sources are in the same directory +set(NANA_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_include_directories(${PROJECT_NAME} PRIVATE ${NANA_INCLUDE_DIR}) + +set(NANA_INCLUDE_SUBDIRS + /. + /filesystem + /gui + /gui/detail + /gui/widgets + /gui/widgets/skeletons + /paint + /paint/detail + /pat + /system + /threads + ) +if(NANA_CMAKE_ENABLE_AUDIO) + list(APPEND NANA_INCLUDE_SUBDIRS + /audio + /audio/detail + ) +endif() + +foreach(subdir ${NANA_INCLUDE_SUBDIRS}) + aux_source_directory(${NANA_INCLUDE_DIR}/nana${subdir} HEADERS) +endforeach() + +# Install the include directories too. +if(NANA_CMAKE_INSTALL_INCLUDES) + target_sources(${PROJECT_NAME} PRIVATE ${HEADERS}) + install(DIRECTORY ${NANA_INCLUDE_DIR}/nana DESTINATION include) + foreach(subdir ${NANA_INCLUDE_SUBDIRS}) + aux_source_directory($ PUBLIC_HEADERS) + endforeach() + target_sources(${PROJECT_NAME} INTERFACE ${PUBLIC_HEADERS}) + message("The Nana include files will be installed in ${CMAKE_INSTALL_PREFIX}/include") +else() + target_sources(${PROJECT_NAME} PUBLIC ${HEADERS}) +endif() + + + target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_14) # 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) -# Installing: the static "nana lib" will be in DESTDIR/CMAKE_INSTALL_PREFIX/lib/ -# and the includes files "include/nana/" in DESTDIR/CMAKE_INSTALL_PREFIX/include/nana/ -# unfortunatelly install() is still ignored by CLion: -# https://intellij-support.jetbrains.com/hc/en-us/community/posts/205822949-CMake-install-isn-t-supported- -install(TARGETS ${PROJECT_NAME} ARCHIVE DESTINATION lib +message("") +message("The compiled Nana library will be installed in ${CMAKE_INSTALL_PREFIX}/lib") +# Actually in DESTDIR/CMAKE_INSTALL_PREFIX/lib but in windows there is no DESTDIR/ part. +install(TARGETS ${PROJECT_NAME} + ARCHIVE DESTINATION lib LIBRARY DESTINATION lib RUNTIME DESTINATION bin) +# test BUILD_SHARED_LIBS directly? 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}" - LINK_FLAGS "${CMAKE_SHARED_LINKER_FLAGS_INIT} -Wl,--output-def=${CMAKE_CURRENT_BINARY_DIR}/lib${PROJECT_NAME}.def") + set_target_properties (${PROJECT_NAME} + PROPERTIES OUTPUT_NAME "${PROJECT_NAME}" + ARCHIVE_OUTPUT_NAME "${PROJECT_NAME}" + LINK_FLAGS "${CMAKE_SHARED_LINKER_FLAGS_INIT} -Wl,--output-def=${CMAKE_CURRENT_BINARY_DIR}/lib${PROJECT_NAME}.def" + ) add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" @@ -106,8 +154,6 @@ if(NANA_CMAKE_SHARED_LIB) 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) @@ -354,6 +400,8 @@ include(CMakePrintHelpers) message ("") cmake_print_variables(SOURCES) +cmake_print_variables(HEADERS) +cmake_print_variables(PUBLIC_HEADERS) message ( "CMAKE_CXX_COMPILER_ID = " ${CMAKE_CXX_COMPILER_ID}) diff --git a/include/nana/basic_types.hpp b/include/nana/basic_types.hpp index a041279b..0821c91b 100644 --- a/include/nana/basic_types.hpp +++ b/include/nana/basic_types.hpp @@ -1,13 +1,13 @@ -/* +/** * Basic Types definition * 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 * http://www.boost.org/LICENSE_1_0.txt) * - * @file: nana/basic_types.hpp + * @file nana/basic_types.hpp */ #ifndef NANA_BASIC_TYPES_HPP From 0309e797016890b266924565051eec0623f40cc0 Mon Sep 17 00:00:00 2001 From: qPCR4vir Date: Thu, 30 Aug 2018 15:05:40 +0200 Subject: [PATCH 023/149] FIX using newer Boost TODO use std::filesystem not experimental --- CMakeLists.txt | 3 +- include/nana/filesystem/filesystem.hpp | 66 ++++++++++---------------- 2 files changed, 28 insertions(+), 41 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2986620b..e8aebcd2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -410,7 +410,8 @@ 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 ("") +cmake_print_variables ( NANA_LINKS ) message ( "DESTDIR = " ${DESTDIR}) message ( "CMAKE_INSTALL_PREFIX = " ${CMAKE_INSTALL_PREFIX}) message ( "NANA_INCLUDE_DIR = " ${NANA_INCLUDE_DIR}) diff --git a/include/nana/filesystem/filesystem.hpp b/include/nana/filesystem/filesystem.hpp index b67a00ef..898fae3c 100644 --- a/include/nana/filesystem/filesystem.hpp +++ b/include/nana/filesystem/filesystem.hpp @@ -13,7 +13,7 @@ * and need VC2015 or a C++11 compiler. With a few correction can be compiler by VC2013 */ -// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4100.pdf --- last pdf of std draft N4100 2014-07-04 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4100.pdf --- pdf of std draft N4100 2014-07-04 // http://en.cppreference.com/w/cpp/experimental/fs // http://cpprocks.com/introduction-to-tr2-filesystem-library-in-vs2012/ --- TR2 filesystem in VS2012 // https://msdn.microsoft.com/en-us/library/hh874694%28v=vs.140%29.aspx --- C++ 14, the header VS2015 @@ -55,36 +55,6 @@ #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 { @@ -105,16 +75,32 @@ 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; - //} +// Boost dont include generic_u8string +// http://www.boost.org/doc/libs/1_66_0/boost/filesystem/path.hpp +// +// Boost versions: 1.67.0, 1.66.0, ... 1.56.0 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 +// 1.55.0 NOT enable directory_iterator C++11 range-base for +// http://www.boost.org/doc/libs/1_54_0/boost/filesystem/operations.hpp +#if BOOST_VERSION < 105600 + namespace boost + // 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(); + } +#endif - //inline directory_iterator end(const directory_iterator&) noexcept - //{ - // return {}; - //} } // filesystem } // experimental } // std From 93bbdb4a880fb8e9ba577b0f5e8dab4e8a1e704b Mon Sep 17 00:00:00 2001 From: Jinhao Date: Fri, 31 Aug 2018 01:21:45 +0800 Subject: [PATCH 024/149] fix bug that a linkage error caused by VC2017v15.8 --- include/nana/gui/drawing.hpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/nana/gui/drawing.hpp b/include/nana/gui/drawing.hpp index 06ecece6..d7f7c7be 100644 --- a/include/nana/gui/drawing.hpp +++ b/include/nana/gui/drawing.hpp @@ -23,9 +23,10 @@ namespace nana class drawing :private nana::noncopyable { + struct draw_fn_handle; public: - typedef struct{}* diehard_t; ///< A handle to a drawing method - typedef std::function draw_fn_t; ///< A function to draw + using diehard_t = draw_fn_handle * ; ///< A handle to a drawing method + using draw_fn_t = std::function; ///< A function to draw drawing(window w); ///< Create a drawing object for a widget w From 745a228dae6beb78fe554c62a9df18b30773c7fd Mon Sep 17 00:00:00 2001 From: Jinhao Date: Fri, 31 Aug 2018 06:27:47 +0800 Subject: [PATCH 025/149] refine the label --- .../widgets/skeletons/text_token_stream.hpp | 1 - source/gui/widgets/label.cpp | 69 ++++++++----------- 2 files changed, 28 insertions(+), 42 deletions(-) diff --git a/include/nana/gui/widgets/skeletons/text_token_stream.hpp b/include/nana/gui/widgets/skeletons/text_token_stream.hpp index f94dfea8..36560067 100644 --- a/include/nana/gui/widgets/skeletons/text_token_stream.hpp +++ b/include/nana/gui/widgets/skeletons/text_token_stream.hpp @@ -682,7 +682,6 @@ namespace nana{ namespace widgets{ namespace skeletons { return lines_.end(); } - private: void _m_parse_format(tokenizer & tknizer, std::stack & fbstack) { diff --git a/source/gui/widgets/label.cpp b/source/gui/widgets/label.cpp index 26be5a95..6d2c6b90 100644 --- a/source/gui/widgets/label.cpp +++ b/source/gui/widgets/label.cpp @@ -20,6 +20,7 @@ #include #include + namespace nana { namespace drawerbase @@ -104,12 +105,10 @@ namespace nana #endif font_ = pre_font; - fblock_ = nullptr; + current_fblock_ = nullptr; _m_set_default(pre_font, fgcolor); - _m_measure(graph); - render_status rs; rs.allowed_width = graph.size().width; @@ -144,15 +143,10 @@ namespace nana else rs.pos.y = 0; - auto vsline_iterator = content_lines.begin(); - for (auto & line : dstream_) + for (auto & line : content_lines) { - if (rs.pos.y >= static_cast(graph.height())) - break; - rs.index = 0; - rs.vslines.clear(); - rs.vslines.swap(*vsline_iterator++); + rs.vslines.swap(line); rs.pos.x = rs.vslines.front().x_base; if (!_m_foreach_visual_line(graph, rs)) @@ -161,7 +155,12 @@ namespace nana rs.pos.y += static_cast(rs.vslines.back().extent_height_px); } - graph.typeface(pre_font); + if (font_ != pre_font) + { + graph.typeface(pre_font); + font_.release(); + current_fblock_ = nullptr; + } } bool find(int x, int y, std::wstring& target, std::wstring& url) const noexcept @@ -183,7 +182,7 @@ namespace nana { ::nana::size retsize; - auto ft = graph.typeface(); //used for restoring the font + auto pre_font = graph.typeface(); //used for restoring the font #ifdef _nana_std_has_string_view const unsigned def_line_pixels = graph.text_extent_size(std::wstring_view(L" ", 1)).height; @@ -191,11 +190,10 @@ namespace nana const unsigned def_line_pixels = graph.text_extent_size(L" ", 1).height; #endif - font_ = ft; - fblock_ = nullptr; + font_ = pre_font; + current_fblock_ = nullptr; - _m_set_default(ft, colors::black); - _m_measure(graph); + _m_set_default(pre_font, colors::black); render_status rs; @@ -218,6 +216,12 @@ namespace nana retsize.height += static_cast(vsline.extent_height_px); } + if (font_ != pre_font) + { + graph.typeface(pre_font); + font_.release(); + current_fblock_ = nullptr; + } return retsize; } private: @@ -294,7 +298,7 @@ namespace nana void _m_change_font(graph_reference graph, nana::widgets::skeletons::fblock* fp) { - if(fp != fblock_) + if (fp != current_fblock_) { auto& name = _m_fontname(fp); auto fontsize = _m_font_size(fp); @@ -307,26 +311,7 @@ namespace nana font_ = paint::font{ name, fontsize, fs }; graph.typeface(font_); } - fblock_ = fp; - } - } - - void _m_measure(graph_reference graph) - { - nana::paint::font ft = font_; - for (auto & line : dstream_) - { - for (auto & value : line) - { - _m_change_font(graph, value.fblock_ptr); - value.data_ptr->measure(graph); - } - } - if(font_ != ft) - { - font_ = ft; - graph.typeface(ft); - fblock_ = nullptr; + current_fblock_ = fp; } } @@ -379,7 +364,10 @@ namespace nana for (auto i = line.cbegin(); i != line.cend(); ++i) { auto const data = i->data_ptr; - auto fblock = i->fblock_ptr; + auto const fblock = i->fblock_ptr; + + _m_change_font(graph, fblock); + data->measure(graph); abs_text_px += data->size().width; @@ -457,9 +445,7 @@ namespace nana if (data->is_text()) { - _m_change_font(graph, fblock); //Split a text into multiple lines - auto rest_extent_size = extent_size.width; std::size_t text_begin = 0; while (text_begin < data->text().size()) { @@ -643,7 +629,8 @@ namespace nana private: dstream dstream_; bool format_enabled_ = false; - ::nana::widgets::skeletons::fblock * fblock_ = nullptr; + + ::nana::widgets::skeletons::fblock * current_fblock_{ nullptr }; ::std::deque traceable_; ::nana::paint::font font_; From 986c6355481fb80c43db39b5f5beaa679ef5a274 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Sun, 2 Sep 2018 07:49:43 +0800 Subject: [PATCH 026/149] refactoring label --- source/gui/widgets/label.cpp | 188 ++++++++++++++++------------------- 1 file changed, 86 insertions(+), 102 deletions(-) diff --git a/source/gui/widgets/label.cpp b/source/gui/widgets/label.cpp index 6d2c6b90..569271e8 100644 --- a/source/gui/widgets/label.cpp +++ b/source/gui/widgets/label.cpp @@ -83,7 +83,7 @@ namespace nana dstream_.parse(s, format_enabled_); } - bool format(bool fm) + bool format(bool fm) noexcept { if (fm == format_enabled_) return false; @@ -96,53 +96,31 @@ namespace nana { traceable_.clear(); - auto pre_font = graph.typeface(); //used for restoring the font - -#ifdef _nana_std_has_string_view - const unsigned def_line_pixels = graph.text_extent_size(std::wstring_view{ L" ", 1 }).height; -#else - const unsigned def_line_pixels = graph.text_extent_size(L" ", 1).height; -#endif - - font_ = pre_font; - current_fblock_ = nullptr; - - _m_set_default(pre_font, fgcolor); - render_status rs; rs.allowed_width = graph.size().width; rs.text_align = th; rs.text_align_v = tv; + ::nana::size extent_size; + //All visual lines data of whole text. - std::deque> content_lines; + auto content_lines = _m_measure_extent_size(graph, th, tv, true, graph.size().width, extent_size); - std::size_t extent_v_pixels = 0; //the pixels, in height, that text will be painted. - - for (auto & line : dstream_) + if ((tv != align_v::top) && extent_size.height < graph.height()) { - _m_prepare_visual_lines(graph, line, def_line_pixels, rs); + rs.pos.y = static_cast(graph.height() - extent_size.height); - for (auto & vsline : rs.vslines) - extent_v_pixels += vsline.extent_height_px; - - content_lines.emplace_back(std::move(rs.vslines)); - - if(extent_v_pixels >= graph.height()) - break; - } - - if((tv != align_v::top) && extent_v_pixels < graph.height()) - { - rs.pos.y = static_cast(graph.height() - extent_v_pixels); - - if(align_v::center == tv) + if (align_v::center == tv) rs.pos.y >>= 1; } else rs.pos.y = 0; + auto pre_font = graph.typeface(); //used for restoring the font + _m_set_default(pre_font, fgcolor); + + for (auto & line : content_lines) { rs.index = 0; @@ -155,19 +133,19 @@ namespace nana rs.pos.y += static_cast(rs.vslines.back().extent_height_px); } - if (font_ != pre_font) + if (transient_.current_font != pre_font) { graph.typeface(pre_font); - font_.release(); - current_fblock_ = nullptr; + transient_.current_font.release(); + transient_.current_fblock = nullptr; } } - bool find(int x, int y, std::wstring& target, std::wstring& url) const noexcept + bool find(const point& mouse_pos, std::wstring& target, std::wstring& url) const { for (auto & t : traceable_) { - if(t.r.is_hit(x, y)) + if(t.r.is_hit(mouse_pos)) { target = t.target; url = t.url; @@ -180,49 +158,10 @@ namespace nana ::nana::size measure(graph_reference graph, unsigned limited, align th, align_v tv) { - ::nana::size retsize; + ::nana::size extent_size; + _m_measure_extent_size(graph, th, tv, false, limited, extent_size); - auto pre_font = graph.typeface(); //used for restoring the font - -#ifdef _nana_std_has_string_view - const unsigned def_line_pixels = graph.text_extent_size(std::wstring_view(L" ", 1)).height; -#else - const unsigned def_line_pixels = graph.text_extent_size(L" ", 1).height; -#endif - - font_ = pre_font; - current_fblock_ = nullptr; - - _m_set_default(pre_font, colors::black); - - render_status rs; - - rs.allowed_width = limited; - rs.text_align = th; - rs.text_align_v = tv; - - for(auto & line: dstream_) - { - rs.vslines.clear(); - auto w = _m_prepare_visual_lines(graph, line, def_line_pixels, rs); - - if(limited && (w > limited)) - w = limited; - - if(retsize.width < w) - retsize.width = w; - - for (auto& vsline : rs.vslines) - retsize.height += static_cast(vsline.extent_height_px); - } - - if (font_ != pre_font) - { - graph.typeface(pre_font); - font_.release(); - current_fblock_ = nullptr; - } - return retsize; + return extent_size; } private: //Manage the fblock for a specified rectangle if it is a traceable fblock. @@ -250,6 +189,9 @@ namespace nana def_.font_size = ft.size(); def_.font_bold = ft.bold(); def_.fgcolor = fgcolor; + + transient_.current_font = ft; + transient_.current_fblock = nullptr; } const ::nana::color& _m_fgcolor(nana::widgets::skeletons::fblock* fp) noexcept @@ -298,20 +240,20 @@ namespace nana void _m_change_font(graph_reference graph, nana::widgets::skeletons::fblock* fp) { - if (fp != current_fblock_) + if (fp != transient_.current_fblock) { auto& name = _m_fontname(fp); auto fontsize = _m_font_size(fp); bool bold = _m_bold(fp); - if((fontsize != font_.size()) || bold != font_.bold() || name != font_.name()) + if((fontsize != transient_.current_font.size()) || bold != transient_.current_font.bold() || name != transient_.current_font.name()) { paint::font::font_style fs; fs.weight = (bold ? 800 : 400); - font_ = paint::font{ name, fontsize, fs }; - graph.typeface(font_); + transient_.current_font = paint::font{ name, fontsize, fs }; + graph.typeface(transient_.current_font); } - current_fblock_ = fp; + transient_.current_fblock = fp; } } @@ -331,6 +273,57 @@ namespace nana } } + std::deque> _m_measure_extent_size(graph_reference graph, nana::align text_align, nana::align_v text_align_v, bool only_screen, unsigned allowed_width_px, nana::size & extent_size) + { + auto pre_font = graph.typeface(); //used for restoring the font + + unsigned text_ascent, text_descent, text_ileading; + graph.text_metrics(text_ascent, text_descent, text_ileading); + + auto const def_line_pixels = text_ascent + text_descent; + + _m_set_default(pre_font, colors::black); + + render_status rs; + + rs.allowed_width = allowed_width_px; + rs.text_align = text_align; + rs.text_align_v = text_align_v; + + //All visual lines data of whole text. + std::deque> content_lines; + + extent_size.width = extent_size.height = 0; + + for (auto & line : dstream_) + { + auto width_px = _m_prepare_visual_lines(graph, line, def_line_pixels, rs); + + if (width_px > extent_size.width) + extent_size.width = width_px; + + for (auto & vsline : rs.vslines) + extent_size.height += vsline.extent_height_px; + + content_lines.emplace_back(std::move(rs.vslines)); + + if (only_screen && (extent_size.height >= graph.height())) + break; + } + + if (allowed_width_px < extent_size.width) + extent_size.width = allowed_width_px; + + if (transient_.current_font != pre_font) + { + graph.typeface(pre_font); + transient_.current_font.release(); + transient_.current_fblock = nullptr; + } + + return content_lines; + } + /** * prepare data for rendering a line of text. */ @@ -579,8 +572,7 @@ namespace nana if (data->is_text()) { - auto const text = data->text().c_str() + vsline_elm.range.first; - auto const reordered = unicode_reorder(text, vsline_elm.range.second); + auto const reordered = unicode_reorder(data->text().c_str() + vsline_elm.range.first, vsline_elm.range.second); _m_change_font(graph, fblock); for (auto & bidi : reordered) @@ -614,26 +606,18 @@ namespace nana rs.pos.x += static_cast(data->size().width); } } - - static std::pair _m_locate(dstream::linecontainer::iterator& i, std::size_t pos) - { - std::size_t n = i->data_ptr->text().length(); - while(pos >= n) - { - pos -= n; - n = (++i)->data_ptr->text().length(); - } - - return{ pos, n - pos }; - } private: dstream dstream_; bool format_enabled_ = false; - ::nana::widgets::skeletons::fblock * current_fblock_{ nullptr }; ::std::deque traceable_; - ::nana::paint::font font_; + struct transient + { + widgets::skeletons::fblock * current_fblock{ nullptr }; + paint::font current_font; + }transient_; + struct def_font_tag { ::std::string font_name; @@ -731,7 +715,7 @@ namespace nana { std::wstring target, url; - if(impl_->renderer.find(arg.pos.x, arg.pos.y, target, url)) + if(impl_->renderer.find(arg.pos, target, url)) { int cur_state = 0; if(target != impl_->target) From de2ec3928da7cbe9589db36f7406cd1fda7baa67 Mon Sep 17 00:00:00 2001 From: qPCR4vir Date: Thu, 6 Sep 2018 10:49:54 +0200 Subject: [PATCH 027/149] modernize CMakeList and make it posible to just include nana --- CMakeLists.txt | 212 +++++++++++++------------- source/detail/mswin/platform_spec.hpp | 4 +- 2 files changed, 108 insertions(+), 108 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e8aebcd2..304a446f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,30 +17,34 @@ # https://cliutils.gitlab.io/modern-cmake/ # https://cmake.org/cmake-tutorial/ -# https://cmake.org/cmake/help/v3.3/module/CMakeDependentOption.html?highlight=cmakedependentoption +# https://cmake.org/cmake/help/v3.13/module/CMakeDependentOption.html?highlight=cmakedependentoption # use CACHE FORCE or set(ENABLE_MINGW_STD_THREADS_WITH_MEGANZ ON) or delete CMakecache.txt or the entirely build dir # if your changes don't execute # It seems that project() defines essential system variables like CMAKE_FIND_LIBRARY_PREFIXES. # https://bbs.archlinux.org/viewtopic.php?id=84967 cmake_minimum_required(VERSION 3.12) -project(nana VERSION 1.6.1 +project(nana VERSION 1.6.2 DESCRIPTION "C++ GUI library" HOMEPAGE_URL http://nanapro.org LANGUAGES CXX ) -# instead of option(NANA_CMAKE_SHARED_LIB "Compile nana as a shared library." OFF) ?? option(BUILD_SHARED_LIBS "Compile nana as a shared library." OFF) -set(NANA_CMAKE_SHARED_LIB ${BUILD_SHARED_LIBS}) - ####################### Main setting of Nana targets, sources and installs -add_library(${PROJECT_NAME} ) +add_library(nana) +target_compile_features(nana PUBLIC cxx_std_14) ### collect all source sub-directories in a list to avoid duplication here -set(NANA_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/source) + +# By using CMAKE_CURRENT_LIST_DIR here you can compile and consume nana by just: +# include(path/to/nana/CMakeLists.txt) +# in your own CMakeLists.txt, and them : +# target_link_libraries(yourApp PRIVATE nana ) + +set(NANA_SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/source) set(NANA_SOURCE_SUBDIRS /. @@ -64,17 +68,16 @@ endif() # collect all source files in the source-sub-dir foreach(subdir ${NANA_SOURCE_SUBDIRS}) - aux_source_directory(${NANA_SOURCE_DIR}${subdir} SOURCES) + aux_source_directory(${NANA_SOURCE_DIR}${subdir} SOURCES) # todo: use GLOB to add headers too ?? endforeach() -target_sources(${PROJECT_NAME} PRIVATE ${SOURCES}) +target_sources(nana PRIVATE ${SOURCES}) -### collect all headers sub-directories in a list to avoid duplication here +### collect all headers sub-directories in a list to avoid duplication here ?? # To show .h files in Visual Studio, add them to the list of sources in add_executable / add_library / target_sources # and Use SOURCE_GROUP if all your sources are in the same directory -set(NANA_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include) -target_include_directories(${PROJECT_NAME} PRIVATE ${NANA_INCLUDE_DIR}) +set(NANA_INCLUDE_DIR ${CMAKE_CURRENT_LIST_DIR}/include) set(NANA_INCLUDE_SUBDIRS /. @@ -97,79 +100,68 @@ if(NANA_CMAKE_ENABLE_AUDIO) endif() foreach(subdir ${NANA_INCLUDE_SUBDIRS}) - aux_source_directory(${NANA_INCLUDE_DIR}/nana${subdir} HEADERS) + aux_source_directory(${NANA_INCLUDE_DIR}/nana${subdir} HEADERS) # todo: use GLOB to add headers too !!!!!!! endforeach() # Install the include directories too. if(NANA_CMAKE_INSTALL_INCLUDES) - target_sources(${PROJECT_NAME} PRIVATE ${HEADERS}) - install(DIRECTORY ${NANA_INCLUDE_DIR}/nana DESTINATION include) - foreach(subdir ${NANA_INCLUDE_SUBDIRS}) + target_sources(nana PRIVATE ${HEADERS}) + target_include_directories(nana PRIVATE ${NANA_INCLUDE_DIR}) + install(DIRECTORY ${NANA_INCLUDE_DIR}/nana DESTINATION include) # in ${CMAKE_INSTALL_PREFIX}/include/nana + foreach(subdir ${NANA_INCLUDE_SUBDIRS}) # this works? aux_source_directory($ PUBLIC_HEADERS) - endforeach() - target_sources(${PROJECT_NAME} INTERFACE ${PUBLIC_HEADERS}) + endforeach() # todo: use GLOB to add headers too !!!!!!! + target_sources(nana INTERFACE ${PUBLIC_HEADERS}) message("The Nana include files will be installed in ${CMAKE_INSTALL_PREFIX}/include") else() - target_sources(${PROJECT_NAME} PUBLIC ${HEADERS}) + target_sources(nana PUBLIC ${HEADERS}) + target_include_directories(nana PUBLIC ${NANA_INCLUDE_DIR}) endif() -target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_14) # -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) message("") message("The compiled Nana library will be installed in ${CMAKE_INSTALL_PREFIX}/lib") # Actually in DESTDIR/CMAKE_INSTALL_PREFIX/lib but in windows there is no DESTDIR/ part. -install(TARGETS ${PROJECT_NAME} +install(TARGETS nana ARCHIVE DESTINATION lib LIBRARY DESTINATION lib RUNTIME DESTINATION bin) -# test BUILD_SHARED_LIBS directly? -if(NANA_CMAKE_SHARED_LIB) +if(BUILD_SHARED_LIBS) # ?? 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}" - LINK_FLAGS "${CMAKE_SHARED_LINKER_FLAGS_INIT} -Wl,--output-def=${CMAKE_CURRENT_BINARY_DIR}/lib${PROJECT_NAME}.def" + set_target_properties (nana PROPERTIES + OUTPUT_NAME nana + ARCHIVE_OUTPUT_NAME nana + LINK_FLAGS "${CMAKE_SHARED_LINKER_FLAGS_INIT} -Wl,--output-def=${CMAKE_CURRENT_BINARY_DIR}/libnana.def" ) - add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + add_custom_command(TARGET nana POST_BUILD WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" COMMAND echo " Generating import library" - COMMAND "${DLLTOOL}" --dllname "lib${PROJECT_NAME}.dll" - --input-def "lib${PROJECT_NAME}.def" - --output-lib "lib${PROJECT_NAME}.lib") + COMMAND "${DLLTOOL}" --dllname "libnana.dll" + --input-def "libnana.def" + --output-lib "libnana.lib") - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/lib${PROJECT_NAME}.def" - "${CMAKE_CURRENT_BINARY_DIR}/lib${PROJECT_NAME}.lib" DESTINATION lib) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libnana.def" + "${CMAKE_CURRENT_BINARY_DIR}/libnana.lib" DESTINATION lib) endif() endif() endif() -# Install the include directories too. -if(NANA_CMAKE_INSTALL_INCLUDES) - install(DIRECTORY ${NANA_INCLUDE_DIR}/nana DESTINATION include) - message("The Nana include files will be installed in ${CMAKE_INSTALL_PREFIX}/include") -endif() - - ######## Nana options -add_definitions(-DNANA_IGNORE_CONF) +target_compile_definitions(nana PRIVATE NANA_IGNORE_CONF) if(NANA_CMAKE_VERBOSE_PREPROCESSOR) - add_definitions(-DVERBOSE_PREPROCESSOR) + target_compile_definitions(nana PRIVATE VERBOSE_PREPROCESSOR) endif() if(NANA_CMAKE_AUTOMATIC_GUI_TESTING) - add_definitions(-DNANA_AUTOMATIC_GUI_TESTING) - enable_testing() + target_compile_definitions(nana PUBLIC NANA_AUTOMATIC_GUI_TESTING) + enable_testing() # ?? endif() @@ -204,9 +196,11 @@ option(NANA_CMAKE_STD_FILESYSTEM_FORCE "Use of STD filesystem?(a compilation err option(NANA_CMAKE_BOOST_FILESYSTEM_FORCE "Force use of Boost filesystem if available (over STD)?" OFF) ########### OS +# https://blog.kowalczyk.info/article/j/guide-to-predefined-macros-in-c-compilers-gcc-clang-msvc-etc..html +# http://nadeausoftware.com/articles/2012/01/c_c_tip_how_use_compiler_predefined_macros_detect_operating_system if(WIN32) - add_definitions(-DWIN32) + target_compile_definitions(nana PUBLIC WIN32) # todo: why not simple test for _WIN32 in code?? # 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) @@ -214,14 +208,14 @@ if(WIN32) # Change the MSVC Compiler flags if(MSVC_USE_MP) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") + target_compile_options(nana PUBLIC "/MP" ) endif() if(MSVC_USE_STATIC_RUNTIME) foreach(flag - CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE - CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO - CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE + CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE + CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO + CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) if(${flag} MATCHES "/MD") string(REGEX REPLACE "/MD" "/MT" ${flag} "${${flag}}") @@ -231,9 +225,9 @@ if(WIN32) 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) + if(NANA_CMAKE_ENABLE_MINGW_STD_THREADS_WITH_MEGANZ) # deprecated ????? + target_compile_definitions(nana PUBLIC STD_THREAD_NOT_SUPPORTED) + target_compile_definitions(nana PUBLIC NANA_ENABLE_MINGW_STD_THREADS_WITH_MEGANZ) endif() endif() @@ -249,20 +243,20 @@ if(WIN32) endif() if(APPLE) - add_definitions(-DAPPLE) - include_directories(/opt/X11/include/) - list(APPEND NANA_LINKS -L/opt/X11/lib/ -liconv) + target_compile_definitions(nana PUBLIC APPLE) # ??? not added by compilers? use __APPLE__ ? + target_include_directories(nana PUBLIC /opt/X11/include/) + target_compile_options(nana PUBLIC -L/opt/X11/lib/ -liconv) set(ENABLE_AUDIO OFF) elseif(UNIX) - add_definitions(-Dlinux) + target_compile_definitions(nana PUBLIC linux) # todo: eliminate. Added by compilers. Also __linux posible endif() if(UNIX) - list(APPEND NANA_LINKS -lX11) + target_compile_options(nana PUBLIC -lX11) include(FindFreetype) if(FREETYPE_FOUND) - include_directories( ${FREETYPE_INCLUDE_DIRS}) - list(APPEND NANA_LINKS -lXft -lfontconfig) + target_include_directories(nana PUBLIC ${FREETYPE_INCLUDE_DIRS}) + target_compile_options(nana PUBLIC -lXft -lfontconfig) endif() endif() @@ -280,41 +274,32 @@ endif() # 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") + + target_compile_options(nana PRIVATE -Wall) if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") if("${CMAKE_SYSTEM_NAME}" MATCHES "FreeBSD") - set(CMAKE_CXX_FLAGS "-std=gnu++14 -Wall -I/usr/local/include") - else() - set(CMAKE_CXX_FLAGS "-std=gnu++14 -Wall") + target_compile_options(nana PRIVATE -I/usr/local/include) endif() - 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) + if(BUILD_SHARED_LIBS) + target_compile_options(nana PRIVATE -lgcc -lstdc++) else() + if(MINGW) - set(CMAKE_EXE_LINKER_FLAGS "-static -pthread") + target_compile_options(nana PRIVATE -static) # -static ?? cmake knows BUILD_SHARED_LIBS else() - set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++ -pthread") + target_compile_options(nana PRIVATE -static-libgcc -static-libstdc++) endif() - endif(NANA_CMAKE_SHARED_LIB) + endif(BUILD_SHARED_LIBS) + + target_compile_options(nana PRIVATE -pthread) - if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0) - # GCC 4.9 - list(APPEND NANA_LINKS "-lboost_system -lboost_thread") - elseif(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.3) - # IS_GNUCXX < 5.3 - else() - list(APPEND NANA_LINKS -lstdc++fs) - endif() endif() if (APPLE AND "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") # APPLE Clang - list(APPEND NANA_LINKS -stdlib=libstdc++) + target_compile_options(nana PRIVATE -stdlib=libstdc++) endif () @@ -325,38 +310,37 @@ if(NANA_CMAKE_ENABLE_PNG) if(NANA_CMAKE_LIBPNG_FROM_OS) find_package(PNG) if(PNG_FOUND) - include_directories(${PNG_INCLUDE_DIRS}) - list(APPEND NANA_LINKS ${PNG_LIBRARIES}) - add_definitions(-DNANA_ENABLE_PNG -DUSE_LIBPNG_FROM_OS) + target_include_directories(nana PRIVATE ${PNG_INCLUDE_DIRS}) + target_compile_options (nana PRIVATE ${PNG_LIBRARIES}) + target_compile_definitions(nana PRIVATE NANA_ENABLE_PNG USE_LIBPNG_FROM_OS) endif() else() - add_definitions(-DNANA_ENABLE_PNG) + target_compile_definitions(nana PRIVATE NANA_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) - include_directories( ${JPEG_INCLUDE_DIR}) - list(APPEND NANA_LINKS ${JPEG_LIBRARY}) - add_definitions(-DNANA_ENABLE_JPEG -DUSE_LIBJPEG_FROM_OS) + target_include_directories(nana PRIVATE ${JPEG_INCLUDE_DIR}) + target_compile_options (nana PRIVATE ${JPEG_LIBRARY}) + target_compile_definitions(nana PRIVATE NANA_ENABLE_JPEG USE_LIBJPEG_FROM_OS) endif() else() - add_definitions(-DNANA_ENABLE_JPEG) + target_compile_definitions(nana PRIVATE NANA_ENABLE_JPEG) endif() endif() # Find ASOUND if(NANA_CMAKE_ENABLE_AUDIO) - add_definitions(-DNANA_ENABLE_AUDIO) + target_compile_definitions(nana PRIVATE NANA_ENABLE_AUDIO) if(UNIX) find_package(ASOUND) if(ASOUND_FOUND) - include_directories(${ASOUND_INCLUDE_DIRS}) - list(APPEND NANA_LINKS -lasound) + target_include_directories(nana PRIVATE ${ASOUND_INCLUDE_DIRS}) + target_compile_options(nana PRIVATE -lasound) else() message(FATAL_ERROR "libasound is not found") endif() @@ -364,37 +348,46 @@ if(NANA_CMAKE_ENABLE_AUDIO) endif() # Find/Select filesystem + + if(NANA_CMAKE_NANA_FILESYSTEM_FORCE) - add_definitions(-DNANA_FILESYSTEM_FORCE) + target_compile_definitions(nana PUBLIC NANA_FILESYSTEM_FORCE) + elseif(NANA_CMAKE_STD_FILESYSTEM_FORCE) - add_definitions(-DSTD_FILESYSTEM_FORCE) + target_compile_definitions(nana PUBLIC STD_FILESYSTEM_FORCE) + target_compile_options (nana PUBLIC -lstdc++fs) + elseif(NANA_CMAKE_FIND_BOOST_FILESYSTEM OR NANA_CMAKE_BOOST_FILESYSTEM_FORCE) if(NANA_CMAKE_BOOST_FILESYSTEM_FORCE) - add_definitions(-DBOOST_FILESYSTEM_FORCE) + target_compile_definitions(nana PUBLIC BOOST_FILESYSTEM_FORCE) + else() + target_compile_options (nana PUBLIC -lstdc++fs) 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) - add_definitions(-DBOOST_FILESYSTEM_AVAILABLE) - include_directories(SYSTEM "${Boost_INCLUDE_DIR}") - list(APPEND NANA_LINKS ${Boost_LIBRARIES}) + target_compile_definitions(nana PUBLIC BOOST_FILESYSTEM_AVAILABLE) + target_include_directories(nana PUBLIC "${Boost_INCLUDE_DIR}") # ?? SYSTEM + target_link_libraries (nana PUBLIC ${Boost_LIBRARIES}) endif() set(Boost_USE_STATIC_LIBS ON) set(Boost_USE_STATIC_RUNTIME ON) endif() + + if("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") - add_definitions(-fmax-errors=3) + target_compile_options(nana PRIVATE -fmax-errors=3) endif() set(CMAKE_DEBUG_POSTFIX "_d") - # Just for information: include(CMakePrintHelpers) @@ -410,8 +403,15 @@ 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 ("") -cmake_print_variables ( NANA_LINKS ) + + +# see: https://cmake.org/cmake/help/v3.12/manual/cmake-properties.7.html#properties-on-targets +cmake_print_properties(TARGETS nana PROPERTIES + COMPILE_DEFINITIONS COMPILE_OPTIONS COMPILE_FLAGS LINK_LIBRARIES + INCLUDE_DIRECTORIES INSTALL_NAME_DIR LINK_FLAGS VERSION + ) + + message ( "DESTDIR = " ${DESTDIR}) message ( "CMAKE_INSTALL_PREFIX = " ${CMAKE_INSTALL_PREFIX}) message ( "NANA_INCLUDE_DIR = " ${NANA_INCLUDE_DIR}) diff --git a/source/detail/mswin/platform_spec.hpp b/source/detail/mswin/platform_spec.hpp index 3505f6e3..1a44ce8b 100644 --- a/source/detail/mswin/platform_spec.hpp +++ b/source/detail/mswin/platform_spec.hpp @@ -1,4 +1,4 @@ -/* +/** * Platform Specification Implementation * Nana C++ Library(http://www.nanapro.org) * Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) @@ -7,7 +7,7 @@ * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) * - * @file: nana/detail/platform_spec.hpp + * @file nana/detail/platform_spec.hpp * * This file provides basis class and data structrue that required by nana * This file should not be included by any header files. From 6fab0f535479695790c71398ccb1ac2bdad893bc Mon Sep 17 00:00:00 2001 From: qPCR4vir Date: Thu, 6 Sep 2018 13:12:46 +0200 Subject: [PATCH 028/149] simplify cmake usage by nana-demo including nana --- .gitignore | 1 + .travis.yml | 7 ------- CMakeLists.txt | 8 ++++---- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index 6bd33ab0..003f80b0 100644 --- a/.gitignore +++ b/.gitignore @@ -42,6 +42,7 @@ lib/ CMakeCache.txt CMakeFiles/ cmake-build-debug/ +cmake-build-*/ .idea/ cmake_install.cmake *.DS_Store diff --git a/.travis.yml b/.travis.yml index 69624c16..09436717 100644 --- a/.travis.yml +++ b/.travis.yml @@ -56,18 +56,11 @@ before_script : - sleep 3 # give xvfb some time to start # we have: qPCR4vir/nana/../nana-demo and now we are in: qPCR4vir/nana/ our executable tests will access: ../nana-demo/Examples/*.bmp etc.(need to be in parallel with nana-demo/Examples) #- cd ../nana-demo - - mkdir ../nana_lib - - mkdir ../nana_demo_bin - cd ../nana_lib - mkdir bin - cd bin script: - # Installing: the static "nana lib" will be in DESTDIR/CMAKE_INSTALL_PREFIX/lib/ - # and the includes files "nana" in DESTDIR/CMAKE_INSTALL_PREFIX/include/ - # we are in "... nana/../nana_lib/bin/" we need "../../nana" to get the CMakeList.txt of nana. - # Thus, make install will put the nana.lib in "... nana/../nana_lib/lib/" - # and the includes in "... nana/../nana_lib/include/" - cmake -G"Unix Makefiles" ../../nana -DCMAKE_INSTALL_PREFIX=.. -DNANA_CMAKE_ENABLE_JPEG=ON -DNANA_CMAKE_ENABLE_PNG=OFF -DNANA_CMAKE_BUILD_DEMOS=ON -DNANA_CMAKE_ENABLE_AUDIO=OFF -DNANA_CMAKE_FIND_BOOST_FILESYSTEM=ON -DNANA_CMAKE_BOOST_FILESYSTEM_FORCE=OFF -DNANA_CMAKE_AUTOMATIC_GUI_TESTING=ON - make install - ls diff --git a/CMakeLists.txt b/CMakeLists.txt index 304a446f..6a2622c3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,7 @@ # https://cliutils.gitlab.io/modern-cmake/ # https://cmake.org/cmake-tutorial/ -# https://cmake.org/cmake/help/v3.13/module/CMakeDependentOption.html?highlight=cmakedependentoption +# https://cmake.org/cmake/help/v3.12/module/CMakeDependentOption.html?highlight=cmakedependentoption # use CACHE FORCE or set(ENABLE_MINGW_STD_THREADS_WITH_MEGANZ ON) or delete CMakecache.txt or the entirely build dir # if your changes don't execute # It seems that project() defines essential system variables like CMAKE_FIND_LIBRARY_PREFIXES. @@ -312,7 +312,7 @@ if(NANA_CMAKE_ENABLE_PNG) if(PNG_FOUND) target_include_directories(nana PRIVATE ${PNG_INCLUDE_DIRS}) target_compile_options (nana PRIVATE ${PNG_LIBRARIES}) - target_compile_definitions(nana PRIVATE NANA_ENABLE_PNG USE_LIBPNG_FROM_OS) + target_compile_definitions(nana PUBLIC NANA_ENABLE_PNG USE_LIBPNG_FROM_OS) endif() else() target_compile_definitions(nana PRIVATE NANA_ENABLE_PNG) @@ -326,7 +326,7 @@ if(NANA_CMAKE_ENABLE_JPEG) if(JPEG_FOUND) target_include_directories(nana PRIVATE ${JPEG_INCLUDE_DIR}) target_compile_options (nana PRIVATE ${JPEG_LIBRARY}) - target_compile_definitions(nana PRIVATE NANA_ENABLE_JPEG USE_LIBJPEG_FROM_OS) + target_compile_definitions(nana PUBLIC NANA_ENABLE_JPEG USE_LIBJPEG_FROM_OS) endif() else() target_compile_definitions(nana PRIVATE NANA_ENABLE_JPEG) @@ -335,7 +335,7 @@ endif() # Find ASOUND if(NANA_CMAKE_ENABLE_AUDIO) - target_compile_definitions(nana PRIVATE NANA_ENABLE_AUDIO) + target_compile_definitions(nana PUBLIC NANA_ENABLE_AUDIO) if(UNIX) find_package(ASOUND) if(ASOUND_FOUND) From c1b88c430a69045f98bac87be87ec6a99ad3f406 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Sat, 8 Sep 2018 18:01:48 +0800 Subject: [PATCH 029/149] add new scroll_into_view() for treebox --- include/nana/gui/widgets/treebox.hpp | 30 ++- source/gui/widgets/treebox.cpp | 314 +++++++++++++++++++-------- 2 files changed, 239 insertions(+), 105 deletions(-) diff --git a/include/nana/gui/widgets/treebox.hpp b/include/nana/gui/widgets/treebox.hpp index 671851c2..55847816 100644 --- a/include/nana/gui/widgets/treebox.hpp +++ b/include/nana/gui/widgets/treebox.hpp @@ -87,13 +87,15 @@ namespace nana class trigger :public drawer_trigger { - template - struct basic_implement; + //template + //struct basic_implement; //deprecated - class item_renderer; + class implementation; + + //class item_renderer; //deprecated class item_locator; - typedef basic_implement implement; + //typedef basic_implement implement; //deprecated public: struct treebox_node_type { @@ -116,20 +118,22 @@ namespace nana trigger(); ~trigger(); - implement * impl() const; + implementation * impl() const; void check(node_type*, checkstate); - void renderer(::nana::pat::cloneable&&); - const ::nana::pat::cloneable& renderer() const; + pat::cloneable& renderer() const; + + //void renderer(::nana::pat::cloneable&&); + //const ::nana::pat::cloneable& renderer() const; //deprecated void placer(::nana::pat::cloneable&&); const ::nana::pat::cloneable& placer() const; node_type* insert(node_type*, const std::string& key, std::string&&); node_type* insert(const std::string& path, std::string&&); - node_type * selected() const; - void selected(node_type*); + //node_type * selected() const; //deprecated + //void selected(node_type*); node_image_tag& icon(const ::std::string&) const; void icon_erase(const ::std::string&); @@ -137,6 +141,7 @@ namespace nana unsigned node_width(const node_type*) const; bool rename(node_type*, const char* key, const char* name); + private: //Overrides drawer_trigger methods void attached(widget_reference, graph_reference) override; @@ -152,7 +157,7 @@ namespace nana void key_press(graph_reference, const arg_keyboard&) override; void key_char(graph_reference, const arg_keyboard&) override; private: - implement * const impl_; + implementation * const impl_; }; //end class trigger @@ -378,7 +383,7 @@ namespace nana template treebox & renderer(const ItemRenderer & rd) ///< set user-defined node renderer { - get_drawer_trigger().renderer(::nana::pat::cloneable(rd)); + get_drawer_trigger().renderer() = ::nana::pat::cloneable{rd}; return *this; } @@ -445,6 +450,9 @@ namespace nana item_proxy selected() const; ///< returns the selected node + /// Scrolls a specified item into view + void scroll_into_view(item_proxy item, align_v); + private: std::shared_ptr _m_scroll_operation() override; diff --git a/source/gui/widgets/treebox.cpp b/source/gui/widgets/treebox.cpp index 3cbf61b7..f0d33a27 100644 --- a/source/gui/widgets/treebox.cpp +++ b/source/gui/widgets/treebox.cpp @@ -180,13 +180,13 @@ namespace nana } };//end class tooltip_window - //item_locator should be defined before the definition of basic_implement + //item_locator should be defined before the definition of implementation class trigger::item_locator { public: using node_type = tree_cont_type::node_type; - item_locator(implement * impl, int item_pos, int x, int y); + item_locator(implementation * impl, int item_pos, int x, int y); int operator()(node_type &node, int affect); node_type * node() const; component what() const; @@ -194,7 +194,7 @@ namespace nana nana::rectangle text_pos() const; private: - trigger::implement * impl_; + implementation * const impl_; nana::point item_pos_; const nana::point pos_; //Mouse pointer position component what_; @@ -212,11 +212,89 @@ namespace nana } }; - //struct implement + //struct implementation //@brief: some data for treebox trigger - template - struct trigger::basic_implement + class trigger::implementation { + class item_rendering_director + : public compset_interface + { + public: + using node_type = tree_cont_type::node_type; + + item_rendering_director(implementation * impl, const nana::point& pos): + impl_(impl), + pos_(pos) + { + } + + //affect + //0 = Sibling, the last is a sibling of node + //1 = Owner, the last is the owner of node + //>=2 = Children, the last is a child of a node that before this node. + int operator()(const node_type& node, int affect) + { + iterated_node_ = &node; + switch (affect) + { + case 1: + pos_.x += impl_->shape.indent_pixels; + break; + default: + if (affect >= 2) + pos_.x -= impl_->shape.indent_pixels * (affect - 1); + } + + auto & comp_placer = impl_->data.comp_placer; + + impl_->assign_node_attr(node_attr_, iterated_node_); + node_r_.x = node_r_.y = 0; + node_r_.width = comp_placer->item_width(*impl_->data.graph, node_attr_); + node_r_.height = comp_placer->item_height(*impl_->data.graph); + + auto renderer = impl_->data.renderer; + renderer->begin_paint(*impl_->data.widget_ptr); + renderer->bground(*impl_->data.graph, this); + renderer->expander(*impl_->data.graph, this); + renderer->crook(*impl_->data.graph, this); + renderer->icon(*impl_->data.graph, this); + renderer->text(*impl_->data.graph, this); + + pos_.y += node_r_.height; + + if (pos_.y > static_cast(impl_->data.graph->height())) + return 0; + + return (node.child && node.value.second.expanded ? 1 : 2); + } + private: + //Overrides compset_interface + virtual const item_attribute_t& item_attribute() const override + { + return node_attr_; + } + + virtual bool comp_attribute(component_t comp, comp_attribute_t& attr) const override + { + attr.area = node_r_; + if (impl_->data.comp_placer->locate(comp, node_attr_, &attr.area)) + { + attr.mouse_pointed = node_attr_.mouse_pointed; + attr.area.x += pos_.x; + attr.area.y += pos_.y; + return true; + } + return false; + } + private: + implementation * const impl_; + ::nana::point pos_; + const node_type * iterated_node_; + item_attribute_t node_attr_; + ::nana::rectangle node_r_; + }; + + public: using node_type = trigger::node_type; struct rep_tag @@ -271,7 +349,7 @@ namespace nana nana::timer timer; }adjust; public: - basic_implement() + implementation() { data.graph = nullptr; data.widget_ptr = nullptr; @@ -353,6 +431,11 @@ namespace nana return true; } + static constexpr unsigned margin_top_bottom() + { + return 1; + } + bool draw(bool reset_scroll, bool ignore_update = false, bool ignore_auto_draw = false) { if(data.graph && (false == data.stop_drawing)) @@ -368,7 +451,7 @@ namespace nana data.graph->rectangle(true, data.widget_ptr->bgcolor()); //Draw tree - attr.tree_cont.for_each(shape.first, Renderer(this, nana::point(static_cast(attr.tree_cont.indent_size(shape.first) * shape.indent_pixels) - shape.offset_x, 1))); + attr.tree_cont.for_each(shape.first, item_rendering_director(this, nana::point(static_cast(attr.tree_cont.indent_size(shape.first) * shape.indent_pixels) - shape.offset_x, margin_top_bottom()))); if (!ignore_update) API::update_window(data.widget_ptr->handle()); @@ -460,6 +543,41 @@ namespace nana return nullptr; } + node_type* last(bool ignore_folded_children) const + { + auto p = attr.tree_cont.get_root(); + + while (true) + { + while (p->next) + p = p->next; + + if (p->child) + { + if (p->value.second.expanded || !ignore_folded_children) + { + p = p->child; + continue; + } + } + + break; + } + + return p; + } + + std::size_t screen_capacity(bool completed) const + { + auto const item_px = data.comp_placer->item_height(*data.graph); + auto screen_px = data.graph->size().height - (margin_top_bottom() << 1); + + if (completed || ((screen_px % item_px) == 0)) + return screen_px / item_px; + + return screen_px / item_px + 1; + } + bool make_adjust(node_type * node, int reason) { if(!node) return false; @@ -1356,7 +1474,7 @@ namespace nana //class trigger::item_locator - trigger::item_locator::item_locator(implement * impl, int item_pos, int x, int y) + trigger::item_locator::item_locator(implementation * impl, int item_pos, int x, int y) : impl_(impl), item_pos_(item_pos, 1), pos_(x, y), @@ -1439,86 +1557,6 @@ namespace nana return{node_text_r_.x + item_pos_.x, node_text_r_.y + item_pos_.y, node_text_r_.width, node_text_r_.height}; } //end class item_locator - - class trigger::item_renderer - : public compset_interface - { - public: - typedef tree_cont_type::node_type node_type; - - item_renderer(implement * impl, const nana::point& pos) - : impl_(impl), - pos_(pos) - { - } - - //affect - //0 = Sibling, the last is a sibling of node - //1 = Owner, the last is the owner of node - //>=2 = Children, the last is a child of a node that before this node. - int operator()(const node_type& node, int affect) - { - implement * draw_impl = impl_; - - iterated_node_ = &node; - switch(affect) - { - case 1: - pos_.x += draw_impl->shape.indent_pixels; - break; - default: - if(affect >= 2) - pos_.x -= draw_impl->shape.indent_pixels * (affect - 1); - } - - auto & comp_placer = impl_->data.comp_placer; - - impl_->assign_node_attr(node_attr_, iterated_node_); - node_r_.x = node_r_.y = 0; - node_r_.width = comp_placer->item_width(*impl_->data.graph, node_attr_); - node_r_.height = comp_placer->item_height(*impl_->data.graph); - - auto renderer = draw_impl->data.renderer; - renderer->begin_paint(*draw_impl->data.widget_ptr); - renderer->bground(*draw_impl->data.graph, this); - renderer->expander(*draw_impl->data.graph, this); - renderer->crook(*draw_impl->data.graph, this); - renderer->icon(*draw_impl->data.graph, this); - renderer->text(*draw_impl->data.graph, this); - - pos_.y += node_r_.height; - - if(pos_.y > static_cast(draw_impl->data.graph->height())) - return 0; - - return (node.child && node.value.second.expanded ? 1 : 2); - } - private: - //Overrides compset_interface - virtual const item_attribute_t& item_attribute() const override - { - return node_attr_; - } - - virtual bool comp_attribute(component_t comp, comp_attribute_t& attr) const override - { - attr.area = node_r_; - if (impl_->data.comp_placer->locate(comp, node_attr_, &attr.area)) - { - attr.mouse_pointed = node_attr_.mouse_pointed; - attr.area.x += pos_.x; - attr.area.y += pos_.y; - return true; - } - return false; - } - private: - trigger::implement * impl_; - ::nana::point pos_; - const node_type * iterated_node_; - item_attribute_t node_attr_; - ::nana::rectangle node_r_; - }; } //Treebox Implementation @@ -1548,7 +1586,7 @@ namespace nana //end struct treebox_node_type trigger::trigger() - : impl_(new implement) + : impl_(new implementation) { impl_->data.trigger_ptr = this; impl_->data.renderer = nana::pat::cloneable(internal_renderer()); @@ -1615,7 +1653,7 @@ namespace nana delete impl_; } - trigger::implement * trigger::impl() const + trigger::implementation * trigger::impl() const { return impl_; } @@ -1687,6 +1725,7 @@ namespace nana } } + /* //deprecated void trigger::renderer(::nana::pat::cloneable&& r) { impl_->data.renderer = std::move(r); @@ -1696,6 +1735,12 @@ namespace nana { return impl_->data.renderer; } + */ + + ::nana::pat::cloneable& trigger::renderer() const + { + return impl_->data.renderer; + } void trigger::placer(::nana::pat::cloneable&& r) { @@ -1729,16 +1774,20 @@ namespace nana return x; } - trigger::node_type* trigger::selected() const + /* + trigger::node_type* trigger::selected() const //deprecated { return impl_->node_state.selected; } + */ - void trigger::selected(node_type* node) + /* + void trigger::selected(node_type* node) //deprecated { if(impl_->attr.tree_cont.verify(node) && impl_->set_selected(node)) impl_->draw(true); } + */ node_image_tag& trigger::icon(const std::string& id) const { @@ -2233,7 +2282,84 @@ namespace nana treebox::item_proxy treebox::selected() const { - return item_proxy(const_cast(&get_drawer_trigger()), get_drawer_trigger().selected()); + //return item_proxy(const_cast(&get_drawer_trigger()), get_drawer_trigger().selected()); //deprecated + auto dw = &get_drawer_trigger(); + return item_proxy(const_cast(dw), dw->impl()->node_state.selected); + } + + void treebox::scroll_into_view(item_proxy item, align_v align) + { + auto node = item._m_node(); + internal_scope_guard lock; + auto impl = get_drawer_trigger().impl(); + + auto & tree = impl->attr.tree_cont; + + auto parent = node->owner; + + std::vector parent_path; + while (parent) + { + parent_path.push_back(parent); + parent = parent->owner; + } + + //Expands the shrinked nodes which are ancestors of node + for (auto i = parent_path.rbegin(); i != parent_path.rend(); ++i) + { + if (!(*i)->value.second.expanded) + { + (*i)->value.second.expanded = true; + item_proxy iprx(impl->data.trigger_ptr, *i); + impl->data.widget_ptr->events().expanded.emit(::nana::arg_treebox{ *impl->data.widget_ptr, iprx, true }, impl->data.widget_ptr->handle()); + } + } + + auto pos = tree.distance_if(node, drawerbase::treebox::pred_allow_child{}); + auto last_pos = tree.distance_if(impl->last(true), drawerbase::treebox::pred_allow_child{}); + + auto const capacity = impl->screen_capacity(false); + auto const item_px = impl->data.comp_placer->item_height(*impl->data.graph); + + if (align_v::top == align) + { + if (last_pos - pos + 1 < capacity) + { + if (last_pos + 1 >= capacity) + pos = last_pos + 1 - capacity; + else + pos = 0; + } + } + else if (align_v::center == align) + { + auto const short_side = (std::min)(pos, last_pos - pos); + if (short_side >= capacity / 2) + { + pos -= short_side; + } + else + { + if (short_side == pos || (last_pos + 1 < capacity)) + pos = 0; + else + pos = last_pos + 1 - capacity; + } + } + else if (align_v::bottom == align) + { + if (pos + 1 >= capacity) + pos = pos + 1 - capacity; + else + pos = 0; + } + + impl->shape.first = impl->attr.tree_cont.advance_if(nullptr, pos, drawerbase::treebox::pred_allow_child{}); + + impl->draw(true, false, true); + + API::update_window(*this); + } std::shared_ptr treebox::_m_scroll_operation() From 074b96a05a1dbba6885f4382c8c80aac00c5a784 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Sun, 9 Sep 2018 05:52:59 +0800 Subject: [PATCH 030/149] add new overloading of scroll_into_view for treebox --- include/nana/gui/widgets/treebox.hpp | 14 ++- source/gui/widgets/treebox.cpp | 170 ++++++++++++++++----------- 2 files changed, 112 insertions(+), 72 deletions(-) diff --git a/include/nana/gui/widgets/treebox.hpp b/include/nana/gui/widgets/treebox.hpp index 55847816..de7eae57 100644 --- a/include/nana/gui/widgets/treebox.hpp +++ b/include/nana/gui/widgets/treebox.hpp @@ -450,8 +450,18 @@ namespace nana item_proxy selected() const; ///< returns the selected node - /// Scrolls a specified item into view - void scroll_into_view(item_proxy item, align_v); + /// Scrolls a specified item into view. + /** + * @param item An item to be requested. + * @param bearing The position where the item to be positioned in the view. + */ + void scroll_into_view(item_proxy item, align_v bearing); + + /// Scrolls a specified item into view. + /** + * @param item An item to be requested. + */ + void scroll_into_view(item_proxy item); private: std::shared_ptr _m_scroll_operation() override; diff --git a/source/gui/widgets/treebox.cpp b/source/gui/widgets/treebox.cpp index f0d33a27..7719ffe7 100644 --- a/source/gui/widgets/treebox.cpp +++ b/source/gui/widgets/treebox.cpp @@ -578,6 +578,96 @@ namespace nana return screen_px / item_px + 1; } + bool scroll_into_view(node_type* node, bool use_bearing, align_v bearing) + { + auto & tree = attr.tree_cont; + + auto parent = node->owner; + + std::vector parent_path; + while (parent) + { + parent_path.push_back(parent); + parent = parent->owner; + } + + bool has_expanded = false; + + //Expands the shrinked nodes which are ancestors of node + for (auto i = parent_path.rbegin(); i != parent_path.rend(); ++i) + { + if (!(*i)->value.second.expanded) + { + has_expanded = true; + (*i)->value.second.expanded = true; + item_proxy iprx(data.trigger_ptr, *i); + data.widget_ptr->events().expanded.emit(::nana::arg_treebox{ *data.widget_ptr, iprx, true }, data.widget_ptr->handle()); + } + } + + auto pos = tree.distance_if(node, pred_allow_child{}); + auto last_pos = tree.distance_if(last(true), pred_allow_child{}); + + auto const capacity = screen_capacity(true); + auto const item_px = data.comp_placer->item_height(*data.graph); + + //If use_bearing is false, it calculates a bearing depending on the current + //position of the requested item. + if (!use_bearing) + { + auto first_pos = tree.distance_if(shape.first, pred_allow_child{}); + + if (pos < first_pos) + bearing = align_v::top; + else if (pos >= first_pos + capacity) + bearing = align_v::bottom; + else + { + //The item is already in the view. + //Returns true if a draw operation is needed + return has_expanded; + } + } + + if (align_v::top == bearing) + { + if (last_pos - pos + 1 < capacity) + { + if (last_pos + 1 >= capacity) + pos = last_pos + 1 - capacity; + else + pos = 0; + } + } + else if (align_v::center == bearing) + { + auto const short_side = (std::min)(pos, last_pos - pos); + if (short_side >= capacity / 2) + { + pos -= short_side; + } + else + { + if (short_side == pos || (last_pos + 1 < capacity)) + pos = 0; + else + pos = last_pos + 1 - capacity; + } + } + else if (align_v::bottom == bearing) + { + if (pos + 1 >= capacity) + pos = pos + 1 - capacity; + else + pos = 0; + } + + auto prv_first = shape.first; + shape.first = attr.tree_cont.advance_if(nullptr, pos, drawerbase::treebox::pred_allow_child{}); + + return has_expanded || (prv_first != shape.first); + } + bool make_adjust(node_type * node, int reason) { if(!node) return false; @@ -2287,79 +2377,19 @@ namespace nana return item_proxy(const_cast(dw), dw->impl()->node_state.selected); } - void treebox::scroll_into_view(item_proxy item, align_v align) + void treebox::scroll_into_view(item_proxy item, align_v bearing) { - auto node = item._m_node(); internal_scope_guard lock; - auto impl = get_drawer_trigger().impl(); - - auto & tree = impl->attr.tree_cont; - - auto parent = node->owner; - - std::vector parent_path; - while (parent) - { - parent_path.push_back(parent); - parent = parent->owner; - } - - //Expands the shrinked nodes which are ancestors of node - for (auto i = parent_path.rbegin(); i != parent_path.rend(); ++i) - { - if (!(*i)->value.second.expanded) - { - (*i)->value.second.expanded = true; - item_proxy iprx(impl->data.trigger_ptr, *i); - impl->data.widget_ptr->events().expanded.emit(::nana::arg_treebox{ *impl->data.widget_ptr, iprx, true }, impl->data.widget_ptr->handle()); - } - } - - auto pos = tree.distance_if(node, drawerbase::treebox::pred_allow_child{}); - auto last_pos = tree.distance_if(impl->last(true), drawerbase::treebox::pred_allow_child{}); - - auto const capacity = impl->screen_capacity(false); - auto const item_px = impl->data.comp_placer->item_height(*impl->data.graph); - - if (align_v::top == align) - { - if (last_pos - pos + 1 < capacity) - { - if (last_pos + 1 >= capacity) - pos = last_pos + 1 - capacity; - else - pos = 0; - } - } - else if (align_v::center == align) - { - auto const short_side = (std::min)(pos, last_pos - pos); - if (short_side >= capacity / 2) - { - pos -= short_side; - } - else - { - if (short_side == pos || (last_pos + 1 < capacity)) - pos = 0; - else - pos = last_pos + 1 - capacity; - } - } - else if (align_v::bottom == align) - { - if (pos + 1 >= capacity) - pos = pos + 1 - capacity; - else - pos = 0; - } - - impl->shape.first = impl->attr.tree_cont.advance_if(nullptr, pos, drawerbase::treebox::pred_allow_child{}); - - impl->draw(true, false, true); - - API::update_window(*this); + if(get_drawer_trigger().impl()->scroll_into_view(item._m_node(), true, bearing)) + API::refresh_window(*this); + } + void treebox::scroll_into_view(item_proxy item) + { + internal_scope_guard lock; + //The third argument for scroll_into_view is ignored if the second argument is false. + if(get_drawer_trigger().impl()->scroll_into_view(item._m_node(), false, align_v::center)) + API::refresh_window(*this); } std::shared_ptr treebox::_m_scroll_operation() From 88b0206016a161f6474aaffbba613ca1ebcd05ca Mon Sep 17 00:00:00 2001 From: qPCR4vir Date: Sun, 9 Sep 2018 01:25:43 +0200 Subject: [PATCH 031/149] target_include_directories INTERFACE ${CMAKE_INSTALL_PREFIX}/include)? --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a2622c3..b5803682 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -106,7 +106,8 @@ endforeach() # Install the include directories too. if(NANA_CMAKE_INSTALL_INCLUDES) target_sources(nana PRIVATE ${HEADERS}) - target_include_directories(nana PRIVATE ${NANA_INCLUDE_DIR}) + target_include_directories(nana PRIVATE ${NANA_INCLUDE_DIR} + INTERFACE ${CMAKE_INSTALL_PREFIX}/include) install(DIRECTORY ${NANA_INCLUDE_DIR}/nana DESTINATION include) # in ${CMAKE_INSTALL_PREFIX}/include/nana foreach(subdir ${NANA_INCLUDE_SUBDIRS}) # this works? aux_source_directory($ PUBLIC_HEADERS) From ba530b83773b4b7e7ced38366e897636feec166f Mon Sep 17 00:00:00 2001 From: qPCR4vir Date: Sun, 9 Sep 2018 01:37:21 +0200 Subject: [PATCH 032/149] fixing travis --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 09436717..ea6f6ee9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -56,17 +56,17 @@ before_script : - sleep 3 # give xvfb some time to start # we have: qPCR4vir/nana/../nana-demo and now we are in: qPCR4vir/nana/ our executable tests will access: ../nana-demo/Examples/*.bmp etc.(need to be in parallel with nana-demo/Examples) #- cd ../nana-demo - - cd ../nana_lib - mkdir bin - cd bin script: - - cmake -G"Unix Makefiles" ../../nana -DCMAKE_INSTALL_PREFIX=.. -DNANA_CMAKE_ENABLE_JPEG=ON -DNANA_CMAKE_ENABLE_PNG=OFF -DNANA_CMAKE_BUILD_DEMOS=ON -DNANA_CMAKE_ENABLE_AUDIO=OFF -DNANA_CMAKE_FIND_BOOST_FILESYSTEM=ON -DNANA_CMAKE_BOOST_FILESYSTEM_FORCE=OFF -DNANA_CMAKE_AUTOMATIC_GUI_TESTING=ON + - cmake -G"Unix Makefiles" ../nana -DCMAKE_INSTALL_PREFIX=.. -DNANA_CMAKE_ENABLE_JPEG=ON -DNANA_CMAKE_ENABLE_PNG=OFF -DNANA_CMAKE_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 -DNANA_CMAKE_INSTALL_INCLUDES=OFF - make install - ls - cd .. - ls - cd .. + - mkdir nana_demo_bin - ls - cd nana_demo_bin - cmake -G"Unix Makefiles" ../nana-demo -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_INCLUDE_EXPERIMENTAL_DEMOS=OFF -DNANA_CMAKE_AUTOMATIC_GUI_TESTING=ON From 4f11de3d606742ad5dee8958b1521e9f393d5ee5 Mon Sep 17 00:00:00 2001 From: qPCR4vir Date: Sun, 9 Sep 2018 01:55:21 +0200 Subject: [PATCH 033/149] fixing travis --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ea6f6ee9..b1d0a290 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,7 +41,8 @@ matrix: - llvm-toolchain-precise before_install: - - git clone --depth=1 --branch=cmake-dev https://github.com/qPCR4vir/nana-demo.git ../nana-demo + - cd .. + - git clone --depth=1 --branch=cmake-dev https://github.com/qPCR4vir/nana-demo.git nana-demo - export PATH="$HOME/bin:$PATH" - wget --no-check-certificate --no-clobber -O /tmp/tools/cmake https://cmake.org/files/v3.12/cmake-3.12.0-rc3-Linux-x86_64.sh || true - chmod -R +x /tmp/tools From 8e7985e0e3dbc3347b6dc6409d3ca81406f190fc Mon Sep 17 00:00:00 2001 From: qPCR4vir Date: Sun, 9 Sep 2018 02:06:06 +0200 Subject: [PATCH 034/149] fixing travis --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index b1d0a290..d2e13c71 100644 --- a/.travis.yml +++ b/.travis.yml @@ -65,8 +65,6 @@ script: - make install - ls - cd .. - - ls - - cd .. - mkdir nana_demo_bin - ls - cd nana_demo_bin From 8057bf95e155f9755f02bbbe7124670ddfc4bb6f Mon Sep 17 00:00:00 2001 From: qPCR4vir Date: Sun, 9 Sep 2018 11:49:30 +0200 Subject: [PATCH 035/149] use NANA_CMAKE_INSTALL --- CMakeLists.txt | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b5803682..4dd04dc0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,8 +10,8 @@ # # Nana uses some build systems: MS-VS solution, MAKE, bakefile, codeblock, etc. manually optimized. # Maybe CMake will be used in the future to generate some of them in the central nana repository. -# But by now CMake is just one option and all the other distributed build system -# files/projects are manually writen. This current CMakeList.txt reflect this fact and that is why we don't +# But by now CMake is just one option and all the other build system +# files/projects distributed are manually writen. This current CMakeList.txt reflect this fact and that is why we don't # generate here configurated *.h files or explicitly enumerate the sources files: anyway this CM-list # will be "touched" to force a re-run of cmake. @@ -20,9 +20,9 @@ # https://cmake.org/cmake/help/v3.12/module/CMakeDependentOption.html?highlight=cmakedependentoption # use CACHE FORCE or set(ENABLE_MINGW_STD_THREADS_WITH_MEGANZ ON) or delete CMakecache.txt or the entirely build dir # if your changes don't execute + # It seems that project() defines essential system variables like CMAKE_FIND_LIBRARY_PREFIXES. # https://bbs.archlinux.org/viewtopic.php?id=84967 - cmake_minimum_required(VERSION 3.12) project(nana VERSION 1.6.2 DESCRIPTION "C++ GUI library" @@ -40,7 +40,7 @@ target_compile_features(nana PUBLIC cxx_std_14) ### collect all source sub-directories in a list to avoid duplication here # By using CMAKE_CURRENT_LIST_DIR here you can compile and consume nana by just: -# include(path/to/nana/CMakeLists.txt) +# add_subdirectory(../nana ../cmake-nana-build-${CONFIG} ) or simmilar # in your own CMakeLists.txt, and them : # target_link_libraries(yourApp PRIVATE nana ) @@ -104,32 +104,27 @@ foreach(subdir ${NANA_INCLUDE_SUBDIRS}) endforeach() # Install the include directories too. -if(NANA_CMAKE_INSTALL_INCLUDES) +if(NANA_CMAKE_INSTALL) + # this is the prefered method to consume nana directly with some specific bulid system + # Is your responsability to ensure all compiler options are compatible with the compilation + # of the project linking to the nana lib here generated target_sources(nana PRIVATE ${HEADERS}) - target_include_directories(nana PRIVATE ${NANA_INCLUDE_DIR} - INTERFACE ${CMAKE_INSTALL_PREFIX}/include) + target_include_directories(nana PRIVATE ${NANA_INCLUDE_DIR}) + message("The compiled Nana library will be installed in ${CMAKE_INSTALL_PREFIX}/lib") + # Actually in DESTDIR/CMAKE_INSTALL_PREFIX/lib but in windows there is no DESTDIR/ part. + install(TARGETS nana + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin) install(DIRECTORY ${NANA_INCLUDE_DIR}/nana DESTINATION include) # in ${CMAKE_INSTALL_PREFIX}/include/nana - foreach(subdir ${NANA_INCLUDE_SUBDIRS}) # this works? - aux_source_directory($ PUBLIC_HEADERS) - endforeach() # todo: use GLOB to add headers too !!!!!!! - target_sources(nana INTERFACE ${PUBLIC_HEADERS}) message("The Nana include files will be installed in ${CMAKE_INSTALL_PREFIX}/include") else() + # this is the prefered method to consume nana with cmake target_sources(nana PUBLIC ${HEADERS}) target_include_directories(nana PUBLIC ${NANA_INCLUDE_DIR}) endif() - - -message("") -message("The compiled Nana library will be installed in ${CMAKE_INSTALL_PREFIX}/lib") -# Actually in DESTDIR/CMAKE_INSTALL_PREFIX/lib but in windows there is no DESTDIR/ part. -install(TARGETS nana - ARCHIVE DESTINATION lib - LIBRARY DESTINATION lib - RUNTIME DESTINATION bin) - if(BUILD_SHARED_LIBS) # ?? if(WIN32) set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) @@ -167,7 +162,7 @@ endif() -option(NANA_CMAKE_INSTALL_INCLUDES "Install nana includes when compile the library" ON) +option(NANA_CMAKE_INSTALL "Install nana when compile the library" OFF) option(NANA_CMAKE_ENABLE_MINGW_STD_THREADS_WITH_MEGANZ "replaced boost.thread with meganz's mingw-std-threads." OFF) option(NANA_CMAKE_ENABLE_PNG "Enable the use of PNG" OFF) option(NANA_CMAKE_LIBPNG_FROM_OS "Use libpng from operating system." ON) From 8c6b0d465332f1ac63540782825730f4d025839a Mon Sep 17 00:00:00 2001 From: qPCR4vir Date: Mon, 10 Sep 2018 14:27:56 +0200 Subject: [PATCH 036/149] simplify CMakeList with cmake scripts --- CMakeLists.txt | 168 ++++------------------------ build/cmake/enable_audio.cmake | 15 +++ build/cmake/enable_jpeg.cmake | 17 +++ build/cmake/enable_png.cmake | 17 +++ build/cmake/select_filesystem.cmake | 47 ++++++++ build/cmake/shared_libs.cmake | 30 +++++ include/nana/config.hpp | 2 +- 7 files changed, 149 insertions(+), 147 deletions(-) create mode 100644 build/cmake/enable_audio.cmake create mode 100644 build/cmake/enable_jpeg.cmake create mode 100644 build/cmake/enable_png.cmake create mode 100644 build/cmake/select_filesystem.cmake create mode 100644 build/cmake/shared_libs.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 4dd04dc0..8d88f0be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -103,6 +103,14 @@ foreach(subdir ${NANA_INCLUDE_SUBDIRS}) aux_source_directory(${NANA_INCLUDE_DIR}/nana${subdir} HEADERS) # todo: use GLOB to add headers too !!!!!!! endforeach() + +option(NANA_CMAKE_INSTALL "Install nana when compile the library" OFF) +option(NANA_CMAKE_ENABLE_MINGW_STD_THREADS_WITH_MEGANZ "replaced boost.thread with meganz's mingw-std-threads." OFF) +option(NANA_CMAKE_VERBOSE_PREPROCESSOR "Show annoying debug messages during compilation." ON) +option(NANA_CMAKE_STOP_VERBOSE_PREPROCESSOR "Stop compilation after showing the annoying debug messages." OFF) +option(NANA_CMAKE_AUTOMATIC_GUI_TESTING "Activate automatic GUI testing?" OFF) + + # Install the include directories too. if(NANA_CMAKE_INSTALL) # this is the prefered method to consume nana directly with some specific bulid system @@ -126,71 +134,20 @@ endif() if(BUILD_SHARED_LIBS) # ?? - if(WIN32) - set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) - if(DLLTOOL) - #generate the lib and def files needed by msvc - set_target_properties (nana PROPERTIES - OUTPUT_NAME nana - ARCHIVE_OUTPUT_NAME nana - LINK_FLAGS "${CMAKE_SHARED_LINKER_FLAGS_INIT} -Wl,--output-def=${CMAKE_CURRENT_BINARY_DIR}/libnana.def" - ) - - add_custom_command(TARGET nana POST_BUILD - WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" - COMMAND echo " Generating import library" - COMMAND "${DLLTOOL}" --dllname "libnana.dll" - --input-def "libnana.def" - --output-lib "libnana.lib") - - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libnana.def" - "${CMAKE_CURRENT_BINARY_DIR}/libnana.lib" DESTINATION lib) - endif() - endif() + include(build/cmake/shared_libs.cmake) endif() ######## Nana options -target_compile_definitions(nana PRIVATE NANA_IGNORE_CONF) +target_compile_definitions(nana PRIVATE NANA_IGNORE_CONF) # really ? if(NANA_CMAKE_VERBOSE_PREPROCESSOR) target_compile_definitions(nana PRIVATE VERBOSE_PREPROCESSOR) endif() if(NANA_CMAKE_AUTOMATIC_GUI_TESTING) target_compile_definitions(nana PUBLIC NANA_AUTOMATIC_GUI_TESTING) - enable_testing() # ?? + # todo: enable_testing() # ?? endif() - - -option(NANA_CMAKE_INSTALL "Install nana when compile the library" OFF) -option(NANA_CMAKE_ENABLE_MINGW_STD_THREADS_WITH_MEGANZ "replaced boost.thread with meganz's mingw-std-threads." OFF) -option(NANA_CMAKE_ENABLE_PNG "Enable the use of PNG" OFF) -option(NANA_CMAKE_LIBPNG_FROM_OS "Use libpng from operating system." ON) -option(NANA_CMAKE_ENABLE_JPEG "Enable the use of JPEG" OFF) -option(NANA_CMAKE_LIBJPEG_FROM_OS "Use libjpeg from operating system." ON) -option(NANA_CMAKE_ENABLE_AUDIO "Enable class audio::play for PCM playback." OFF) -option(NANA_CMAKE_VERBOSE_PREPROCESSOR "Show annoying debug messages during compilation." ON) -option(NANA_CMAKE_STOP_VERBOSE_PREPROCESSOR "Stop compilation after showing the annoying debug messages." OFF) -option(NANA_CMAKE_AUTOMATIC_GUI_TESTING "Activate automatic GUI testing?" OFF) - -# The ISO C++ File System Technical Specification (ISO-TS, or STD) is optional. -# http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4100.pdf -# This is not a workaround, but an user option. -# The library maybe available in the std library in use or from Boost (almost compatible) -# http://www.boost.org/doc/libs/1_60_0/libs/filesystem/doc/index.htm -# or you can choose to use the (partial, but functional) implementation provided by nana. -# If you include the file or -# the selected option will be set by nana into std::experimental::filesystem -# By default Nana will try to use the STD. If STD is not available and NANA_CMAKE_FIND_BOOST_FILESYSTEM -# is set to ON nana will try to use boost if available. Nana own implementation will be use if none of -# the previus were selected or available. -# You can change that default if you change one of the following -# (please don't define more than one of the _XX_FORCE options): -option(NANA_CMAKE_FIND_BOOST_FILESYSTEM "Search: Is Boost filesystem available?" OFF) -option(NANA_CMAKE_NANA_FILESYSTEM_FORCE "Force nana filesystem over ISO and boost?" OFF) -option(NANA_CMAKE_STD_FILESYSTEM_FORCE "Use of STD filesystem?(a compilation error will ocurre if not available)" OFF) -option(NANA_CMAKE_BOOST_FILESYSTEM_FORCE "Force use of Boost filesystem if available (over STD)?" OFF) - ########### OS # https://blog.kowalczyk.info/article/j/guide-to-predefined-macros-in-c-compilers-gcc-clang-msvc-etc..html # http://nadeausoftware.com/articles/2012/01/c_c_tip_how_use_compiler_predefined_macros_detect_operating_system @@ -226,16 +183,6 @@ if(WIN32) target_compile_definitions(nana PUBLIC NANA_ENABLE_MINGW_STD_THREADS_WITH_MEGANZ) endif() endif() - - if(MSVC) - set(DLLTOOL OFF) - else() - # mingw: If dlltool is found the def and lib file will be created - find_program (DLLTOOL dlltool) - if(NOT DLLTOOL) - message(WARNING "dlltool not found. Skipping import library generation.") - endif() - endif() endif() if(APPLE) @@ -274,107 +221,36 @@ if(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") target_compile_options(nana PRIVATE -Wall) if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") if("${CMAKE_SYSTEM_NAME}" MATCHES "FreeBSD") - target_compile_options(nana PRIVATE -I/usr/local/include) + target_compile_options(nana PUBLIC -I/usr/local/include) endif() endif() if(BUILD_SHARED_LIBS) - target_compile_options(nana PRIVATE -lgcc -lstdc++) + target_compile_options(nana PUBLIC -lgcc -lstdc++) else() if(MINGW) - target_compile_options(nana PRIVATE -static) # -static ?? cmake knows BUILD_SHARED_LIBS + target_compile_options(nana PUBLIC -static) # -static ?? cmake knows BUILD_SHARED_LIBS else() - target_compile_options(nana PRIVATE -static-libgcc -static-libstdc++) + target_compile_options(nana PUBLIC -static-libgcc -static-libstdc++) endif() endif(BUILD_SHARED_LIBS) - target_compile_options(nana PRIVATE -pthread) + target_compile_options(nana PUBLIC -pthread) endif() if (APPLE AND "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") # APPLE Clang - target_compile_options(nana PRIVATE -stdlib=libstdc++) + target_compile_options(nana PUBLIC -stdlib=libstdc++) endif () ############# Optional libraries - -# Find PNG -if(NANA_CMAKE_ENABLE_PNG) - if(NANA_CMAKE_LIBPNG_FROM_OS) - find_package(PNG) - if(PNG_FOUND) - target_include_directories(nana PRIVATE ${PNG_INCLUDE_DIRS}) - target_compile_options (nana PRIVATE ${PNG_LIBRARIES}) - target_compile_definitions(nana PUBLIC NANA_ENABLE_PNG USE_LIBPNG_FROM_OS) - endif() - else() - target_compile_definitions(nana PRIVATE NANA_ENABLE_PNG) - endif() -endif() - -# Find JPEG -if(NANA_CMAKE_ENABLE_JPEG) - if(NANA_CMAKE_LIBJPEG_FROM_OS) - find_package(JPEG) - if(JPEG_FOUND) - target_include_directories(nana PRIVATE ${JPEG_INCLUDE_DIR}) - target_compile_options (nana PRIVATE ${JPEG_LIBRARY}) - target_compile_definitions(nana PUBLIC NANA_ENABLE_JPEG USE_LIBJPEG_FROM_OS) - endif() - else() - target_compile_definitions(nana PRIVATE NANA_ENABLE_JPEG) - endif() -endif() - -# Find ASOUND -if(NANA_CMAKE_ENABLE_AUDIO) - target_compile_definitions(nana PUBLIC NANA_ENABLE_AUDIO) - if(UNIX) - find_package(ASOUND) - if(ASOUND_FOUND) - target_include_directories(nana PRIVATE ${ASOUND_INCLUDE_DIRS}) - target_compile_options(nana PRIVATE -lasound) - else() - message(FATAL_ERROR "libasound is not found") - endif() - endif() -endif() - -# Find/Select filesystem - - -if(NANA_CMAKE_NANA_FILESYSTEM_FORCE) - target_compile_definitions(nana PUBLIC NANA_FILESYSTEM_FORCE) - -elseif(NANA_CMAKE_STD_FILESYSTEM_FORCE) - target_compile_definitions(nana PUBLIC STD_FILESYSTEM_FORCE) - target_compile_options (nana PUBLIC -lstdc++fs) - -elseif(NANA_CMAKE_FIND_BOOST_FILESYSTEM OR NANA_CMAKE_BOOST_FILESYSTEM_FORCE) - if(NANA_CMAKE_BOOST_FILESYSTEM_FORCE) - target_compile_definitions(nana PUBLIC BOOST_FILESYSTEM_FORCE) - else() - target_compile_options (nana PUBLIC -lstdc++fs) - 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) - target_compile_definitions(nana PUBLIC BOOST_FILESYSTEM_AVAILABLE) - target_include_directories(nana PUBLIC "${Boost_INCLUDE_DIR}") # ?? SYSTEM - target_link_libraries (nana PUBLIC ${Boost_LIBRARIES}) - endif() - set(Boost_USE_STATIC_LIBS ON) - set(Boost_USE_STATIC_RUNTIME ON) -endif() - - +include(build/cmake/enable_png.cmake) +include(build/cmake/enable_jpeg.cmake) +include(build/cmake/enable_audio.cmake) +include(build/cmake/select_filesystem.cmake) if("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") @@ -388,7 +264,7 @@ set(CMAKE_DEBUG_POSTFIX "_d") include(CMakePrintHelpers) message ("") -cmake_print_variables(SOURCES) +# cmake_print_variables(SOURCES) cmake_print_variables(HEADERS) cmake_print_variables(PUBLIC_HEADERS) diff --git a/build/cmake/enable_audio.cmake b/build/cmake/enable_audio.cmake new file mode 100644 index 00000000..ab26967f --- /dev/null +++ b/build/cmake/enable_audio.cmake @@ -0,0 +1,15 @@ +option(NANA_CMAKE_ENABLE_AUDIO "Enable class audio::play for PCM playback." OFF) + +# todo: decide - PUBLIC vs PRIVATE +if(NANA_CMAKE_ENABLE_AUDIO) + target_compile_definitions(nana PUBLIC NANA_ENABLE_AUDIO) + if(UNIX) + find_package(ASOUND) + if(ASOUND_FOUND) + target_include_directories(nana PUBLIC ${ASOUND_INCLUDE_DIRS}) + target_compile_options(nana PUBLIC -lasound) + else() + message(FATAL_ERROR "libasound is not found") + endif() + endif() +endif() \ No newline at end of file diff --git a/build/cmake/enable_jpeg.cmake b/build/cmake/enable_jpeg.cmake new file mode 100644 index 00000000..552a252a --- /dev/null +++ b/build/cmake/enable_jpeg.cmake @@ -0,0 +1,17 @@ +option(NANA_CMAKE_ENABLE_JPEG "Enable the use of JPEG" OFF) +option(NANA_CMAKE_LIBJPEG_FROM_OS "Use libjpeg from operating system." ON) + +# todo: decide - PUBLIC vs PRIVATE + +if(NANA_CMAKE_ENABLE_JPEG) + if(NANA_CMAKE_LIBJPEG_FROM_OS) + find_package(JPEG) + if(JPEG_FOUND) + target_include_directories(nana PUBLIC ${JPEG_INCLUDE_DIR}) + target_compile_options (nana PUBLIC ${JPEG_LIBRARY}) + target_compile_definitions(nana PUBLIC NANA_ENABLE_JPEG USE_LIBJPEG_FROM_OS) + endif() + else() + target_compile_definitions(nana PUBLIC NANA_ENABLE_JPEG) + endif() +endif() \ No newline at end of file diff --git a/build/cmake/enable_png.cmake b/build/cmake/enable_png.cmake new file mode 100644 index 00000000..6d739782 --- /dev/null +++ b/build/cmake/enable_png.cmake @@ -0,0 +1,17 @@ +option(NANA_CMAKE_ENABLE_PNG "Enable the use of PNG" OFF) +option(NANA_CMAKE_LIBPNG_FROM_OS "Use libpng from operating system." ON) + +# todo: decide - PUBLIC vs PRIVATE + +if(NANA_CMAKE_ENABLE_PNG) + if(NANA_CMAKE_LIBPNG_FROM_OS) + find_package(PNG) + if(PNG_FOUND) + target_include_directories(nana PUBLIC ${PNG_INCLUDE_DIRS}) + target_compile_options (nana PUBLIC ${PNG_LIBRARIES}) + target_compile_definitions(nana PUBLIC NANA_ENABLE_PNG USE_LIBPNG_FROM_OS) + endif() + else() + target_compile_definitions(nana PUBLIC NANA_ENABLE_PNG) + endif() +endif() \ No newline at end of file diff --git a/build/cmake/select_filesystem.cmake b/build/cmake/select_filesystem.cmake new file mode 100644 index 00000000..1b54f1e2 --- /dev/null +++ b/build/cmake/select_filesystem.cmake @@ -0,0 +1,47 @@ +# The ISO C++ File System Technical Specification (ISO-TS, or STD) is optional. +# http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4100.pdf +# This is not a workaround, but an user option. +# The library maybe available in the std library in use or from Boost (almost compatible) +# http://www.boost.org/doc/libs/1_60_0/libs/filesystem/doc/index.htm +# or you can choose to use the (partial, but functional) implementation provided by nana. +# If you include the file or +# the selected option will be set by nana into std::experimental::filesystem +# By default Nana will try to use the STD. If STD is not available and NANA_CMAKE_FIND_BOOST_FILESYSTEM +# is set to ON nana will try to use boost if available. Nana own implementation will be use if none of +# the previus were selected or available. +# You can change that default if you change one of the following +# (please don't define more than one of the _XX_FORCE options): +option(NANA_CMAKE_FIND_BOOST_FILESYSTEM "Search: Is Boost filesystem available?" OFF) +option(NANA_CMAKE_NANA_FILESYSTEM_FORCE "Force nana filesystem over ISO and boost?" OFF) +option(NANA_CMAKE_STD_FILESYSTEM_FORCE "Use of STD filesystem?(a compilation error will ocurre if not available)" OFF) +option(NANA_CMAKE_BOOST_FILESYSTEM_FORCE "Force use of Boost filesystem if available (over STD)?" OFF) + +if(NANA_CMAKE_NANA_FILESYSTEM_FORCE) + target_compile_definitions(nana PUBLIC NANA_FILESYSTEM_FORCE) + +elseif(NANA_CMAKE_STD_FILESYSTEM_FORCE) + target_compile_definitions(nana PUBLIC STD_FILESYSTEM_FORCE) + target_compile_options (nana PUBLIC -lstdc++fs) + +elseif(NANA_CMAKE_FIND_BOOST_FILESYSTEM OR NANA_CMAKE_BOOST_FILESYSTEM_FORCE) + if(NANA_CMAKE_BOOST_FILESYSTEM_FORCE) + target_compile_definitions(nana PUBLIC BOOST_FILESYSTEM_FORCE) + else() + target_compile_options (nana PUBLIC -lstdc++fs) + 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) + target_compile_definitions(nana PUBLIC BOOST_FILESYSTEM_AVAILABLE) + target_include_directories(nana PUBLIC "${Boost_INCLUDE_DIR}") # ?? SYSTEM + target_link_libraries (nana PUBLIC ${Boost_LIBRARIES}) + endif() + set(Boost_USE_STATIC_LIBS ON) + set(Boost_USE_STATIC_RUNTIME ON) +endif() + + diff --git a/build/cmake/shared_libs.cmake b/build/cmake/shared_libs.cmake new file mode 100644 index 00000000..6d7d6190 --- /dev/null +++ b/build/cmake/shared_libs.cmake @@ -0,0 +1,30 @@ + if(WIN32) + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) + if(MSVC) + set(DLLTOOL OFF) + else() + # mingw: If dlltool is found the def and lib file will be created + find_program (DLLTOOL dlltool) + if(NOT DLLTOOL) + message(WARNING "dlltool not found. Skipping import library generation.") + endif() + endif() + if(DLLTOOL) + #generate the lib and def files needed by msvc + set_target_properties (nana PROPERTIES + OUTPUT_NAME nana + ARCHIVE_OUTPUT_NAME nana + LINK_FLAGS "${CMAKE_SHARED_LINKER_FLAGS_INIT} -Wl,--output-def=${CMAKE_CURRENT_BINARY_DIR}/libnana.def" + ) + + add_custom_command(TARGET nana POST_BUILD + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + COMMAND echo " Generating import library" + COMMAND "${DLLTOOL}" --dllname "libnana.dll" + --input-def "libnana.def" + --output-lib "libnana.lib") + + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libnana.def" + "${CMAKE_CURRENT_BINARY_DIR}/libnana.lib" DESTINATION lib) + endif() + endif() \ No newline at end of file diff --git a/include/nana/config.hpp b/include/nana/config.hpp index 17c5cdc2..aac360c4 100644 --- a/include/nana/config.hpp +++ b/include/nana/config.hpp @@ -71,7 +71,7 @@ /////////////////// // Support of PCM playback // -#define NANA_ENABLE_AUDIO +//#define NANA_ENABLE_AUDIO /////////////////// // Support for PNG From 1821f3018e5c3d53a054e0b73eb0f3a805e42cc8 Mon Sep 17 00:00:00 2001 From: qPCR4vir Date: Mon, 10 Sep 2018 14:44:42 +0200 Subject: [PATCH 037/149] travis calls nana-demo directly --- .travis.yml | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index d2e13c71..743efe3b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -57,18 +57,11 @@ before_script : - sleep 3 # give xvfb some time to start # we have: qPCR4vir/nana/../nana-demo and now we are in: qPCR4vir/nana/ our executable tests will access: ../nana-demo/Examples/*.bmp etc.(need to be in parallel with nana-demo/Examples) #- cd ../nana-demo - - mkdir bin - - cd bin + - mkdir demo-build + - cd demo-build script: - - 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 -DNANA_CMAKE_INSTALL_INCLUDES=OFF - - make install - - ls - - cd .. - - mkdir nana_demo_bin - - ls - - cd nana_demo_bin - - cmake -G"Unix Makefiles" ../nana-demo -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_INCLUDE_EXPERIMENTAL_DEMOS=OFF -DNANA_CMAKE_AUTOMATIC_GUI_TESTING=ON + - cmake -G"Unix Makefiles" ../nana-demo -DCMAKE_INSTALL_PREFIX=.. -DNANA_CMAKE_ENABLE_JPEG=ON -DNANA_CMAKE_FIND_BOOST_FILESYSTEM=ON -DNANA_CMAKE_AUTOMATIC_GUI_TESTING=ON - make install # todo: separate resources from sources (a directory for images) - ls From aac9ef525a80238d9d54d82d210b5003b6c55be0 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Tue, 11 Sep 2018 07:34:25 +0800 Subject: [PATCH 038/149] fix crash when close owner window window position issue makes popup menu at wrong position --- include/nana/gui/widgets/menubar.hpp | 22 +-------- source/detail/platform_spec_posix.cpp | 26 ++++++---- source/gui/detail/native_window_interface.cpp | 47 +++++++++++++++---- source/gui/widgets/menubar.cpp | 43 ++++++++++++----- 4 files changed, 89 insertions(+), 49 deletions(-) diff --git a/include/nana/gui/widgets/menubar.hpp b/include/nana/gui/widgets/menubar.hpp index 75158284..c6bbd7c9 100644 --- a/include/nana/gui/widgets/menubar.hpp +++ b/include/nana/gui/widgets/menubar.hpp @@ -1,7 +1,7 @@ /* * A Menubar implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2009-2017 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2009-2018 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -34,26 +34,6 @@ namespace nana color_proxy border_highlight{ colors::highlight }; }; - class item_renderer - { - public: - enum class state - { - normal, highlighted, selected - }; - - using graph_reference = paint::graphics&; - using scheme = ::nana::drawerbase::menubar::scheme; - - item_renderer(window, graph_reference); - virtual void background(const point&, const ::nana::size&, state); - virtual void caption(const point&, const native_string_type&); - scheme *scheme_ptr() const { return scheme_ptr_; }; - private: - graph_reference graph_; - scheme *scheme_ptr_; - }; - class trigger : public drawer_trigger { diff --git a/source/detail/platform_spec_posix.cpp b/source/detail/platform_spec_posix.cpp index bf3ab2fb..62cb293a 100644 --- a/source/detail/platform_spec_posix.cpp +++ b/source/detail/platform_spec_posix.cpp @@ -658,21 +658,31 @@ namespace detail platform_scope_guard lock; if(umake_owner(wd)) { + auto & wd_manager = detail::bedrock::instance().wd_manager(); + + std::vector owned_children; + auto i = wincontext_.find(wd); if(i != wincontext_.end()) { if(i->second.owned) { - set_error_handler(); - auto & wd_manager = detail::bedrock::instance().wd_manager(); - for(auto u = i->second.owned->rbegin(); u != i->second.owned->rend(); ++u) - wd_manager.close(wd_manager.root(*u)); - - rev_error_handler(); - - delete i->second.owned; + for(auto child : *i->second.owned) + owned_children.push_back(child); } + } + //Closing a child will erase the wd from the table wincontext_, so the + //iterator i can't be reused after children closed. + set_error_handler(); + for(auto u = owned_children.rbegin(); u != owned_children.rend(); ++u) + wd_manager.close(wd_manager.root(*u)); + rev_error_handler(); + + i = wincontext_.find(wd); + if(i != wincontext_.end()) + { + delete i->second.owned; wincontext_.erase(i); } } diff --git a/source/gui/detail/native_window_interface.cpp b/source/gui/detail/native_window_interface.cpp index ef0b1935..8d3dd9c8 100644 --- a/source/gui/detail/native_window_interface.cpp +++ b/source/gui/detail/native_window_interface.cpp @@ -371,6 +371,8 @@ namespace nana{ } Window parent = (owner ? reinterpret_cast(owner) : restrict::spec.root_window()); + + //The position passed to XCreateWindow is a screen coordinate. nana::point pos(r.x, r.y); if((false == nested) && owner) { @@ -396,7 +398,9 @@ namespace nana{ { auto origin_owner = (owner ? owner : reinterpret_cast(restrict::spec.root_window())); restrict::spec.make_owner(origin_owner, reinterpret_cast(handle)); - exposed_positions[handle] = pos; + + //The exposed_position is a relative position to its owner/parent. + exposed_positions[handle] = r.position(); } XChangeWindowAttributes(disp, handle, attr_mask, &win_attr); @@ -945,13 +949,14 @@ namespace nana{ auto fm_extents = window_frame_extents(wd); origin.x = -fm_extents.left; origin.y = -fm_extents.top; - +#if 0 //deprecated if(reinterpret_cast(coord_wd) != restrict::spec.root_window()) { fm_extents = window_frame_extents(coord_wd); origin.x += fm_extents.left; origin.y += fm_extents.top; } +#endif } else coord_wd = get_window(wd, window_relationship::parent); @@ -1010,8 +1015,14 @@ namespace nana{ if(owner && (owner != reinterpret_cast(restrict::spec.root_window()))) { auto origin = window_position(owner); +#if 0 x += origin.x; y += origin.y; +#else + auto owner_extents = window_frame_extents(owner); + x += origin.x + owner_extents.left; + y += origin.y + owner_extents.top; +#endif } ::XMoveWindow(disp, reinterpret_cast(wd), x, y); @@ -1099,8 +1110,14 @@ namespace nana{ if(owner && (owner != reinterpret_cast(restrict::spec.root_window()))) { auto origin = window_position(owner); +#if 0 x += origin.x; y += origin.y; +#else + auto owner_extents = window_frame_extents(owner); + x += origin.x + owner_extents.left; + y += origin.y + owner_extents.top; +#endif } ::XMoveResizeWindow(disp, reinterpret_cast(wd), x, y, r.width, r.height); @@ -1580,14 +1597,21 @@ namespace nana{ pos.y = point.y; return true; } - return false; #elif defined(NANA_X11) nana::detail::platform_scope_guard psg; int x = pos.x, y = pos.y; Window child; - return (True == ::XTranslateCoordinates(restrict::spec.open_display(), - reinterpret_cast(wd), restrict::spec.root_window(), x, y, &pos.x, &pos.y, &child)); + if(True == ::XTranslateCoordinates(restrict::spec.open_display(), + reinterpret_cast(wd), restrict::spec.root_window(), x, y, &pos.x, &pos.y, &child)) + { + //deprecated + //auto fm_extents = window_frame_extents(wd); + //pos.x += fm_extents.left; + //pos.y += fm_extents.top; + return true; + } #endif + return false; } bool native_interface::calc_window_point(native_window_type wd, nana::point& pos) @@ -1600,14 +1624,21 @@ namespace nana{ pos.y = point.y; return true; } - return false; #elif defined(NANA_X11) nana::detail::platform_scope_guard psg; int x = pos.x, y = pos.y; Window child; - return (True == ::XTranslateCoordinates(restrict::spec.open_display(), - restrict::spec.root_window(), reinterpret_cast(wd), x, y, &pos.x, &pos.y, &child)); + if(True == ::XTranslateCoordinates(restrict::spec.open_display(), restrict::spec.root_window(), reinterpret_cast(wd), x, y, &pos.x, &pos.y, &child)) + { + //deprecated + //Now the origin of pos is the left-top corner of the window(including titlebar and border) + //auto fm_extents = window_frame_extents(wd); + //pos.x += fm_extents.left; + //pos.y += fm_extents.top; + return true; + } #endif + return false; } native_window_type native_interface::find_window(int x, int y) diff --git a/source/gui/widgets/menubar.cpp b/source/gui/widgets/menubar.cpp index 94d7c6c2..aeb0c405 100644 --- a/source/gui/widgets/menubar.cpp +++ b/source/gui/widgets/menubar.cpp @@ -1,7 +1,7 @@ /* * A Menubar implementation * Nana C++ Library(http://www.nanapro.org) -* Copyright(C) 2009-2017 Jinhao(cnjinhao@hotmail.com) +* Copyright(C) 2009-2018 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -215,12 +215,32 @@ namespace nana }; //class item_renderer - item_renderer::item_renderer(window wd, graph_reference graph) - :graph_(graph), scheme_ptr_(static_cast(API::dev::get_scheme(wd))) - {} - - void item_renderer::background(const nana::point& pos, const nana::size& size, state item_state) + class item_renderer + { + public: + enum class state { + normal, highlighted, selected + }; + + using graph_reference = paint::graphics&; + using scheme = ::nana::drawerbase::menubar::scheme; + + item_renderer(window, graph_reference); + virtual void background(const point&, const ::nana::size&, state); + virtual void caption(const point&, const native_string_type&); + scheme *scheme_ptr() const { return scheme_ptr_; }; + private: + graph_reference graph_; + scheme *scheme_ptr_; + }; + + item_renderer::item_renderer(window wd, graph_reference graph) + :graph_(graph), scheme_ptr_(static_cast(API::dev::get_scheme(wd))) + {} + + void item_renderer::background(const nana::point& pos, const nana::size& size, state item_state) + { auto bground = scheme_ptr_->text_fgcolor; ::nana::color border, body; @@ -245,13 +265,12 @@ namespace nana paint::draw{ graph_ }.corner(r, 1); graph_.rectangle(r.pare_off(1), true, body); - } + } - void item_renderer::caption(const point& pos, const native_string_type& text) - { - graph_.string(pos, text, scheme_ptr_->text_fgcolor); - - } + void item_renderer::caption(const point& pos, const native_string_type& text) + { + graph_.string(pos, text, scheme_ptr_->text_fgcolor); + } //end class item_renderer //class trigger From df1059d297efc5c33c900be3c06e5c8db7b4c03a Mon Sep 17 00:00:00 2001 From: qPCR4vir Date: Tue, 11 Sep 2018 16:32:24 +0200 Subject: [PATCH 039/149] cmake_print_variables (NANA_CMAKE_INSTALL) --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8d88f0be..29160e09 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -267,7 +267,7 @@ message ("") # cmake_print_variables(SOURCES) cmake_print_variables(HEADERS) cmake_print_variables(PUBLIC_HEADERS) - +cmake_print_variables(NANA_CMAKE_INSTALL) message ( "CMAKE_CXX_COMPILER_ID = " ${CMAKE_CXX_COMPILER_ID}) message ( "COMPILER_IS_CLANG = " ${COMPILER_IS_CLANG}) From 2de08f8f9fe80fd2793d13c833a97bddc38b2b9d Mon Sep 17 00:00:00 2001 From: Jinhao Date: Thu, 13 Sep 2018 07:27:50 +0800 Subject: [PATCH 040/149] add move-semantics and delete copy-ctor/assignment for animation --- include/nana/gui/animation.hpp | 22 ++++++++-------------- source/gui/animation.cpp | 21 ++++++++++++++++++++- 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/include/nana/gui/animation.hpp b/include/nana/gui/animation.hpp index 9f0df563..e8338399 100644 --- a/include/nana/gui/animation.hpp +++ b/include/nana/gui/animation.hpp @@ -1,7 +1,7 @@ /* * An Animation 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 @@ -49,24 +49,18 @@ namespace nana struct impl; class performance_manager; + + /// Non-copyable + animation(const animation&) = delete; + animation& operator=(const animation&) = delete; public: animation(std::size_t fps = 23); ~animation(); - void push_back(frameset frms); - /* - void branch(const std::string& name, const frameset& frms) - { - impl_->branches[name].frames = frms; - } + animation(animation&&); + animation& operator=(animation&&); - void branch(const std::string& name, const frameset& frms, std::function condition) - { - auto & br = impl_->branches[name]; - br.frames = frms; - br.condition = condition; - } - */ + void push_back(frameset frms); void looped(bool enable); ///< Enables or disables the animation repeating playback. diff --git a/source/gui/animation.cpp b/source/gui/animation.cpp index c737d334..16e54729 100644 --- a/source/gui/animation.cpp +++ b/source/gui/animation.cpp @@ -1,7 +1,7 @@ /* * An Animation 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 @@ -571,6 +571,25 @@ namespace nana delete impl_; } + animation::animation(animation&& rhs) + : impl_(rhs.impl_) + { + rhs.impl_ = new impl(23); + } + + animation& animation::operator=(animation&& rhs) + { + if (this != &rhs) + { + auto imp = new impl{ 23 }; + + delete impl_; + impl_ = rhs.impl_; + rhs.impl_ = imp; + } + return *this; + } + void animation::push_back(frameset frms) { impl_->framesets.emplace_back(std::move(frms)); From a41eb4471406e668b81c807be08022553bb02187 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Mon, 17 Sep 2018 07:48:23 +0800 Subject: [PATCH 041/149] fix bug that label uses all ram when its width is zero. --- source/gui/widgets/label.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/source/gui/widgets/label.cpp b/source/gui/widgets/label.cpp index 569271e8..03b88215 100644 --- a/source/gui/widgets/label.cpp +++ b/source/gui/widgets/label.cpp @@ -311,7 +311,8 @@ namespace nana break; } - if (allowed_width_px < extent_size.width) + //The width is not restricted if the allowed_width_px is zero. + if (allowed_width_px && (allowed_width_px < extent_size.width)) extent_size.width = allowed_width_px; if (transient_.current_font != pre_font) @@ -445,6 +446,10 @@ namespace nana unsigned sub_text_px = 0; auto sub_text_len = _m_fit_text(graph, data->text().substr(text_begin), rs.allowed_width, sub_text_px); + //At least one character must be displayed no matter whether the width is enough or not. + if (0 == sub_text_len) + sub_text_len = 1; + if (text_begin + sub_text_len < data->text().size()) { //make a new visual line From 284a153be023bb9f4c9add2d8f1dae03f5029996 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Wed, 19 Sep 2018 22:37:24 +0800 Subject: [PATCH 042/149] fix bug that treebox scroll_into_view wrongly calcuates a position --- source/gui/widgets/treebox.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/source/gui/widgets/treebox.cpp b/source/gui/widgets/treebox.cpp index 7719ffe7..30739d1c 100644 --- a/source/gui/widgets/treebox.cpp +++ b/source/gui/widgets/treebox.cpp @@ -644,7 +644,7 @@ namespace nana auto const short_side = (std::min)(pos, last_pos - pos); if (short_side >= capacity / 2) { - pos -= short_side; + pos = short_side - capacity / 2; } else { @@ -664,7 +664,10 @@ namespace nana auto prv_first = shape.first; shape.first = attr.tree_cont.advance_if(nullptr, pos, drawerbase::treebox::pred_allow_child{}); - + + //Update the position of scroll + show_scroll(); + return has_expanded || (prv_first != shape.first); } From bbe2aca4834e30a5b15ab41e898ea774f1f7d1af Mon Sep 17 00:00:00 2001 From: Jinhao Date: Fri, 21 Sep 2018 01:47:34 +0800 Subject: [PATCH 043/149] fix bug that treebox.scroll_into_view() generated a wrong position --- source/gui/widgets/treebox.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/source/gui/widgets/treebox.cpp b/source/gui/widgets/treebox.cpp index 30739d1c..195f6900 100644 --- a/source/gui/widgets/treebox.cpp +++ b/source/gui/widgets/treebox.cpp @@ -643,16 +643,11 @@ namespace nana { auto const short_side = (std::min)(pos, last_pos - pos); if (short_side >= capacity / 2) - { - pos = short_side - capacity / 2; - } + pos -= capacity / 2; + else if (short_side == pos || (last_pos + 1 < capacity)) + pos = 0; else - { - if (short_side == pos || (last_pos + 1 < capacity)) - pos = 0; - else - pos = last_pos + 1 - capacity; - } + pos = last_pos + 1 - capacity; } else if (align_v::bottom == bearing) { From 7fea766f99262144d8c8a741906a134bce286456 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Sat, 22 Sep 2018 00:37:36 +0800 Subject: [PATCH 044/149] fix bug that causes dead-lock under Linux --- source/gui/detail/bedrock_posix.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/source/gui/detail/bedrock_posix.cpp b/source/gui/detail/bedrock_posix.cpp index 62738e98..0504cf95 100644 --- a/source/gui/detail/bedrock_posix.cpp +++ b/source/gui/detail/bedrock_posix.cpp @@ -973,7 +973,12 @@ namespace detail case Expose: if(msgwnd->visible && (msgwnd->root_graph->empty() == false)) { - nana::detail::platform_scope_guard lock; + nana::internal_scope_guard lock; + //Don't lock this scope using platform-scope-guard. Because it would cause the platform-scope-lock to be locked + //before the internal-scope-guard, and the order of locking would cause dead-lock. + // + //Locks this scope using internal-scope-guard is correct and safe. In the scope, the Xlib functions aren't called + //directly. if(msgwnd->is_draw_through()) { msgwnd->other.attribute.root->draw_through(); From 2c3d75c7091178056640d914d3efd0518815df92 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Tue, 25 Sep 2018 02:07:15 +0800 Subject: [PATCH 045/149] fix bug of C++ feature detection(#338) --- include/nana/c++defines.hpp | 6 +++--- source/gui/widgets/label.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/nana/c++defines.hpp b/include/nana/c++defines.hpp index ef5d8e9f..9456887f 100644 --- a/include/nana/c++defines.hpp +++ b/include/nana/c++defines.hpp @@ -211,10 +211,10 @@ #undef _nana_std_optional -#if ((defined(_MSC_VER) && (_MSC_VER >= 1912) && ((!defined(_MSVC_LANG)) || _MSVC_LANG < 201703))) || \ - ((__cplusplus < 201703L) || \ +#if ((defined(_MSC_VER) && ((!defined(_MSVC_LANG)) || _MSVC_LANG < 201703))) || \ + ((!defined(_MSC_VER)) && ((__cplusplus < 201703L) || \ (defined(__clang__) && (__clang_major__ * 100 + __clang_minor__ < 400)) || \ - (!defined(__clang__) && defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ < 701)) \ + (!defined(__clang__) && defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ < 701))) \ ) # define _nana_std_optional #endif diff --git a/source/gui/widgets/label.cpp b/source/gui/widgets/label.cpp index 03b88215..88fe88f2 100644 --- a/source/gui/widgets/label.cpp +++ b/source/gui/widgets/label.cpp @@ -303,7 +303,7 @@ namespace nana extent_size.width = width_px; for (auto & vsline : rs.vslines) - extent_size.height += vsline.extent_height_px; + extent_size.height += static_cast(vsline.extent_height_px); content_lines.emplace_back(std::move(rs.vslines)); From b6028f807914d8e3271a2fcc393686fa83212a02 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Mon, 1 Oct 2018 07:43:57 +0800 Subject: [PATCH 046/149] fix bug that font is missing when measure a zero-sized label --- source/gui/widgets/label.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/gui/widgets/label.cpp b/source/gui/widgets/label.cpp index 88fe88f2..74bedf81 100644 --- a/source/gui/widgets/label.cpp +++ b/source/gui/widgets/label.cpp @@ -891,7 +891,8 @@ namespace nana if(graph_ptr->empty()) { graph_ptr = &substitute; - graph_ptr->make({ 10, 10 }); + substitute.make({ 10, 10 }); + substitute.typeface(this->typeface()); } return impl->renderer.measure(*graph_ptr, limited, impl->text_align, impl->text_align_v); From f09e9bcf43eab16b56f9a10f62ac8bdb9179fa42 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Mon, 1 Oct 2018 11:47:33 +0800 Subject: [PATCH 047/149] small changes --- include/nana/gui/widgets/treebox.hpp | 34 ++++++++++++++++++++++------ source/gui/widgets/treebox.cpp | 4 ++-- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/include/nana/gui/widgets/treebox.hpp b/include/nana/gui/widgets/treebox.hpp index de7eae57..ef804ff9 100644 --- a/include/nana/gui/widgets/treebox.hpp +++ b/include/nana/gui/widgets/treebox.hpp @@ -135,7 +135,7 @@ namespace nana //node_type * selected() const; //deprecated //void selected(node_type*); - node_image_tag& icon(const ::std::string&) const; + node_image_tag& icon(const ::std::string&); void icon_erase(const ::std::string&); void node_icon(node_type*, const ::std::string& id); unsigned node_width(const node_type*) const; @@ -176,11 +176,11 @@ namespace nana /// Append a child with a specified value (user object.). template - item_proxy append(const ::std::string& key, ::std::string name, const T&t) + item_proxy append(const ::std::string& key, ::std::string name, T&& t) { item_proxy ip = append(key, std::move(name)); if(false == ip.empty()) - ip.value(t); + ip.value(std::forward(t)); return ip; } @@ -296,17 +296,19 @@ namespace nana return *p; } + /* template - item_proxy & value(const T& t) + item_proxy & value(const T& t) //deprecated { _m_value() = t; return *this; } + */ template item_proxy & value(T&& t) { - _m_value() = std::move(t); + _m_value() = std::forward(t); return *this; } @@ -408,6 +410,23 @@ namespace nana /// @param enable bool whether to enable. void auto_draw(bool enable); + /// Prevents drawing during execution. + template + void avoid_drawing(Function fn) + { + this->auto_draw(false); + try + { + fn(); + } + catch (...) + { + this->auto_draw(true); + throw; + } + this->auto_draw(true); + } + /// \brief Enable the checkboxs for each item of the widget. /// @param enable bool indicates whether to show or hide the checkboxs. treebox & checkable(bool enable); @@ -424,8 +443,9 @@ namespace nana /// These states are 'normal', 'hovered' and 'expanded'. /// If 'hovered' or 'expanded' are not set, it uses 'normal' state image for these 2 states. /// See also in [documentation](http://nanapro.org/en-us/help/widgets/treebox.htm) - node_image_type& icon(const ::std::string& id ///< the name of an icon scheme. If the name is not existing, it creates a new scheme for the name. - ) const; + /// @param id The name of an icon scheme. If the name is not existing, it creates a new scheme for the name. + /// @return The reference of node image scheme correspending with the specified id. + node_image_type& icon(const ::std::string& id); void icon_erase(const ::std::string& id); diff --git a/source/gui/widgets/treebox.cpp b/source/gui/widgets/treebox.cpp index 195f6900..fa8a9ad0 100644 --- a/source/gui/widgets/treebox.cpp +++ b/source/gui/widgets/treebox.cpp @@ -1877,7 +1877,7 @@ namespace nana } */ - node_image_tag& trigger::icon(const std::string& id) const + node_image_tag& trigger::icon(const std::string& id) { auto i = impl_->shape.image_table.find(id); if(i != impl_->shape.image_table.end()) @@ -2304,7 +2304,7 @@ namespace nana impl->draw(true); } - treebox::node_image_type& treebox::icon(const std::string& id) const + treebox::node_image_type& treebox::icon(const std::string& id) { return get_drawer_trigger().icon(id); } From 8c8adb397e926abf283c3af5d7b3c42ec02d0ff8 Mon Sep 17 00:00:00 2001 From: qPCR4vir Date: Mon, 1 Oct 2018 19:04:12 +0200 Subject: [PATCH 048/149] target_link_libraries --- build/cmake/enable_audio.cmake | 2 +- build/cmake/enable_jpeg.cmake | 7 ++++--- build/cmake/enable_png.cmake | 7 ++++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/build/cmake/enable_audio.cmake b/build/cmake/enable_audio.cmake index ab26967f..881b4c24 100644 --- a/build/cmake/enable_audio.cmake +++ b/build/cmake/enable_audio.cmake @@ -7,7 +7,7 @@ if(NANA_CMAKE_ENABLE_AUDIO) find_package(ASOUND) if(ASOUND_FOUND) target_include_directories(nana PUBLIC ${ASOUND_INCLUDE_DIRS}) - target_compile_options(nana PUBLIC -lasound) + target_link_libraries(nana PUBLIC ${ASOUND_LIBRARY}) else() message(FATAL_ERROR "libasound is not found") endif() diff --git a/build/cmake/enable_jpeg.cmake b/build/cmake/enable_jpeg.cmake index 552a252a..09fd9244 100644 --- a/build/cmake/enable_jpeg.cmake +++ b/build/cmake/enable_jpeg.cmake @@ -4,14 +4,15 @@ option(NANA_CMAKE_LIBJPEG_FROM_OS "Use libjpeg from operating system." ON) # todo: decide - PUBLIC vs PRIVATE if(NANA_CMAKE_ENABLE_JPEG) + target_compile_definitions(nana PUBLIC NANA_ENABLE_JPEG) if(NANA_CMAKE_LIBJPEG_FROM_OS) find_package(JPEG) if(JPEG_FOUND) target_include_directories(nana PUBLIC ${JPEG_INCLUDE_DIR}) - target_compile_options (nana PUBLIC ${JPEG_LIBRARY}) - target_compile_definitions(nana PUBLIC NANA_ENABLE_JPEG USE_LIBJPEG_FROM_OS) + target_link_libraries (nana PUBLIC ${JPEG_LIBRARY}) + target_compile_definitions(nana PUBLIC USE_LIBJPEG_FROM_OS) endif() else() - target_compile_definitions(nana PUBLIC NANA_ENABLE_JPEG) + target_compile_definitions(nana PUBLIC -ljpeg) endif() endif() \ No newline at end of file diff --git a/build/cmake/enable_png.cmake b/build/cmake/enable_png.cmake index 6d739782..e60bd02e 100644 --- a/build/cmake/enable_png.cmake +++ b/build/cmake/enable_png.cmake @@ -4,14 +4,15 @@ option(NANA_CMAKE_LIBPNG_FROM_OS "Use libpng from operating system." ON) # todo: decide - PUBLIC vs PRIVATE if(NANA_CMAKE_ENABLE_PNG) + target_compile_definitions(nana PUBLIC NANA_ENABLE_PNG) if(NANA_CMAKE_LIBPNG_FROM_OS) find_package(PNG) if(PNG_FOUND) target_include_directories(nana PUBLIC ${PNG_INCLUDE_DIRS}) - target_compile_options (nana PUBLIC ${PNG_LIBRARIES}) - target_compile_definitions(nana PUBLIC NANA_ENABLE_PNG USE_LIBPNG_FROM_OS) + target_link_libraries (nana PUBLIC ${PNG_LIBRARIES}) + target_compile_definitions(nana PUBLIC USE_LIBPNG_FROM_OS ${PNG_DEFINITIONS}) endif() else() - target_compile_definitions(nana PUBLIC NANA_ENABLE_PNG) + target_compile_definitions(nana PUBLIC -lpng) endif() endif() \ No newline at end of file From 2e0bf06dcb41811f2c4dee1634a6148f176b88e2 Mon Sep 17 00:00:00 2001 From: qPCR4vir Date: Mon, 1 Oct 2018 19:37:33 +0200 Subject: [PATCH 049/149] trying to fix a definition duplication of boolean window.h vs jpeg --- build/cmake/enable_jpeg.cmake | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/build/cmake/enable_jpeg.cmake b/build/cmake/enable_jpeg.cmake index 09fd9244..8fdcb980 100644 --- a/build/cmake/enable_jpeg.cmake +++ b/build/cmake/enable_jpeg.cmake @@ -1,5 +1,6 @@ option(NANA_CMAKE_ENABLE_JPEG "Enable the use of JPEG" OFF) option(NANA_CMAKE_LIBJPEG_FROM_OS "Use libjpeg from operating system." ON) +option(JPEG_HAVE_BOOLEAN "Defining HAVE_BOOLEAN before including jpeglib.h" ON) # todo: decide - PUBLIC vs PRIVATE @@ -15,4 +16,9 @@ if(NANA_CMAKE_ENABLE_JPEG) else() target_compile_definitions(nana PUBLIC -ljpeg) endif() + if(JPEG_HAVE_BOOLEAN) + # ... Defining HAVE_BOOLEAN before including jpeglib.h should make it work... + target_compile_definitions(nana PUBLIC HAVE_BOOLEAN) + endif() + endif() \ No newline at end of file From 4f42041388c834b411ca0595e499d104f4910318 Mon Sep 17 00:00:00 2001 From: qPCR4vir Date: Mon, 1 Oct 2018 19:55:06 +0200 Subject: [PATCH 050/149] fix definition of boolean in jpeg by default --- build/cmake/enable_jpeg.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/cmake/enable_jpeg.cmake b/build/cmake/enable_jpeg.cmake index 8fdcb980..a6956f66 100644 --- a/build/cmake/enable_jpeg.cmake +++ b/build/cmake/enable_jpeg.cmake @@ -1,6 +1,6 @@ option(NANA_CMAKE_ENABLE_JPEG "Enable the use of JPEG" OFF) option(NANA_CMAKE_LIBJPEG_FROM_OS "Use libjpeg from operating system." ON) -option(JPEG_HAVE_BOOLEAN "Defining HAVE_BOOLEAN before including jpeglib.h" ON) +option(JPEG_HAVE_BOOLEAN "Defining HAVE_BOOLEAN before including jpeglib.h" OFF) # todo: decide - PUBLIC vs PRIVATE From b430f8285504dd0be0108c93fa89bd1914fbabee Mon Sep 17 00:00:00 2001 From: qPCR4vir Date: Tue, 2 Oct 2018 18:40:27 +0200 Subject: [PATCH 051/149] delete files for deprecated Biicode --- bii/layout.bii | 12 ------------ bii/policies.bii | 11 ----------- bii/settings.bii | 2 -- biicode.conf | 49 ------------------------------------------------ 4 files changed, 74 deletions(-) delete mode 100644 bii/layout.bii delete mode 100644 bii/policies.bii delete mode 100644 bii/settings.bii delete mode 100644 biicode.conf diff --git a/bii/layout.bii b/bii/layout.bii deleted file mode 100644 index 31e284d2..00000000 --- a/bii/layout.bii +++ /dev/null @@ -1,12 +0,0 @@ - # Minimal layout, with all auxiliary folders inside "bii" and -# The binary "bin" folder as is, and enabled code edition in the project root -cmake: bii/cmake -lib: bii/lib -build: bii/build - -deps: bii/deps -# Setting this to True enables directly editing in the project root -# instead of blocks/youruser/yourblock -# the block will be named as your project folder -auto-root-block: True -root-block: qiangwu/nana \ No newline at end of file diff --git a/bii/policies.bii b/bii/policies.bii deleted file mode 100644 index d6d5e86e..00000000 --- a/bii/policies.bii +++ /dev/null @@ -1,11 +0,0 @@ -# This file configures your finds of dependencies. -# -# It is an ordered list of rules, which will be evaluated in order, of the form: -# block_pattern: TAG -# -# For each possible block that could resolve your dependencies, -# only versions with tag >= TAG will be accepted - -qiangwu/* : DEV -* : STABLE - diff --git a/bii/settings.bii b/bii/settings.bii deleted file mode 100644 index fa648ede..00000000 --- a/bii/settings.bii +++ /dev/null @@ -1,2 +0,0 @@ -cmake: {generator: MinGW Makefiles} -os: {arch: 32bit, family: Windows, subfamily: '7', version: 6.1.7601} diff --git a/biicode.conf b/biicode.conf deleted file mode 100644 index 872c1375..00000000 --- a/biicode.conf +++ /dev/null @@ -1,49 +0,0 @@ -# Biicode configuration file - -[requirements] - glenn/png: 6 - -[parent] - # The parent version of this block. Must match folder name. E.g. - # user/block # No version number means not published yet - # You can change it to publish to a different track, and change version, e.g. - # user/block(track): 7 - qiangwu/nana: 0 - -[paths] - # Local directories to look for headers (within block) - # / - include - -[dependencies] - # Manual adjust file implicit dependencies, add (+), remove (-), or overwrite (=) - # hello.h + hello_imp.cpp hello_imp2.cpp - # *.h + *.cpp - include/nana/config.hpp + include/* - include/nana/config.hpp + source/* - -[mains] - # Manual adjust of files that define an executable - # !main.cpp # Do not build executable from this file - # main2.cpp # Build it (it doesnt have a main() function, but maybe it includes it) - -[tests] - # Manual adjust of files that define a CTest test - # test/* pattern to evaluate this test/ folder sources like tests - -[hooks] - # These are defined equal to [dependencies],files names matching bii*stage*hook.py - # will be launched as python scripts at stage = {post_process, clean} - # CMakeLists.txt + bii/my_post_process1_hook.py bii_clean_hook.py - -[includes] - # Mapping of include patterns to external blocks - # hello*.h: user3/depblock # includes will be processed as user3/depblock/hello*.h - png.h: glenn/png - -[data] - # Manually define data files dependencies, that will be copied to bin for execution - # By default they are copied to bin/user/block/... which should be taken into account - # when loading from disk such data - # image.cpp + image.jpg # code should write open("user/block/image.jpg") - From 2e6a85bf893f46724abffd59aea0bab5a4b2edd0 Mon Sep 17 00:00:00 2001 From: qPCR4vir Date: Tue, 2 Oct 2018 18:47:04 +0200 Subject: [PATCH 052/149] reorganize cmake in small scripts with some fix from https://github.com/cnjinhao/nana/pull/278 --- CMakeLists.txt | 235 ++++++---------------------- build/cmake/OS.cmake | 70 +++++++++ build/cmake/compilers.cmake | 48 ++++++ build/cmake/enable_audio.cmake | 4 +- build/cmake/enable_jpeg.cmake | 6 +- build/cmake/enable_png.cmake | 4 +- build/cmake/install_nana.cmake | 23 +++ build/cmake/select_filesystem.cmake | 24 +-- build/cmake/shared_libs.cmake | 25 ++- cmake/Modules/FindFontconfig.cmake | 69 -------- 10 files changed, 233 insertions(+), 275 deletions(-) create mode 100644 build/cmake/OS.cmake create mode 100644 build/cmake/compilers.cmake create mode 100644 build/cmake/install_nana.cmake delete mode 100644 cmake/Modules/FindFontconfig.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 89295796..2e427ee6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,4 @@ # CMake configuration for Nana -# Author: Andrew Kornilov(https://github.com/ierofant) # Contributors: # Andrew Kornilov (ierofant) - original version # Jinhao @@ -8,7 +7,7 @@ # Robert Hauck - Enable support for PNG/Freetype # Pavel O. - fix compilation with boost::filesystem (#281) # Frostbane - Add option for compiling a shared library (#263,#265) -# Qiangqiang Wu - Add biicode support +# Qiangqiang Wu - Add biicode support: todo migrate to https://conan.io/ # # Nana uses some build systems: MS-VS solution, MAKE, bakefile, codeblock, etc. manually optimized. # Maybe CMake will be used in the future to generate some of them in the central nana repository. @@ -20,6 +19,7 @@ # https://cliutils.gitlab.io/modern-cmake/ # https://cmake.org/cmake-tutorial/ # https://cmake.org/cmake/help/v3.12/module/CMakeDependentOption.html?highlight=cmakedependentoption +# cmake 3.12 have more better modern c++ support cmake_minimum_required(VERSION 3.12 FATAL_ERROR) project(nana VERSION 1.6.2 @@ -27,15 +27,27 @@ project(nana VERSION 1.6.2 HOMEPAGE_URL http://nanapro.org LANGUAGES CXX ) - -option(BUILD_SHARED_LIBS "Compile nana as a shared library." OFF) - -####################### Main setting of Nana targets, sources and installs +####################### Main setting of Nana targets, sources and installs ##################### add_library(nana) -target_compile_features(nana PUBLIC cxx_std_14) +target_compile_features(nana PUBLIC cxx_std_17) -### collect all source sub-directories in a list to avoid duplication here +# need after cxx_std_14 or cxx_std_17 ?? +target_compile_features(nana + PUBLIC cxx_nullptr + PUBLIC cxx_range_for + PUBLIC cxx_lambdas + PUBLIC cxx_decltype_auto + PUBLIC cxx_defaulted_functions + PUBLIC cxx_deleted_functions + PUBLIC cxx_auto_type + PUBLIC cxx_decltype_incomplete_return_types + PUBLIC cxx_defaulted_move_initializers + PUBLIC cxx_noexcept + PUBLIC cxx_rvalue_references + ) + +### collect all source sub-directories in a list to avoid duplication ### # By using CMAKE_CURRENT_LIST_DIR here you can compile and consume nana by just: # add_subdirectory(../nana ../cmake-nana-build-${CONFIG} ) or simmilar @@ -71,8 +83,7 @@ endforeach() target_sources(nana PRIVATE ${SOURCES}) - -### collect all headers sub-directories in a list to avoid duplication here ?? +### collect all headers sub-directories in a list to avoid duplication ### # To show .h files in Visual Studio, add them to the list of sources in add_executable / add_library / target_sources # and Use SOURCE_GROUP if all your sources are in the same directory set(NANA_INCLUDE_DIR ${CMAKE_CURRENT_LIST_DIR}/include) @@ -101,39 +112,11 @@ foreach(subdir ${NANA_INCLUDE_SUBDIRS}) aux_source_directory(${NANA_INCLUDE_DIR}/nana${subdir} HEADERS) # todo: use GLOB to add headers too !!!!!!! endforeach() - -option(NANA_CMAKE_INSTALL "Install nana when compile the library" OFF) -option(NANA_CMAKE_ENABLE_MINGW_STD_THREADS_WITH_MEGANZ "replaced boost.thread with meganz's mingw-std-threads." OFF) -option(NANA_CMAKE_VERBOSE_PREPROCESSOR "Show annoying debug messages during compilation." ON) +### Some nana compilation options ### +option(NANA_CMAKE_VERBOSE_PREPROCESSOR "Show annoying debug messages during compilation." OFF) option(NANA_CMAKE_STOP_VERBOSE_PREPROCESSOR "Stop compilation after showing the annoying debug messages." OFF) option(NANA_CMAKE_AUTOMATIC_GUI_TESTING "Activate automatic GUI testing?" OFF) - - -# Install the include directories too. -if(NANA_CMAKE_INSTALL) - # this is the prefered method to consume nana directly with some specific bulid system - # Is your responsability to ensure all compiler options are compatible with the compilation - # of the project linking to the nana lib here generated - target_sources(nana PRIVATE ${HEADERS}) - target_include_directories(nana PRIVATE ${NANA_INCLUDE_DIR}) - message("The compiled Nana library will be installed in ${CMAKE_INSTALL_PREFIX}/lib") - # Actually in DESTDIR/CMAKE_INSTALL_PREFIX/lib but in windows there is no DESTDIR/ part. - install(TARGETS nana - ARCHIVE DESTINATION lib - LIBRARY DESTINATION lib - RUNTIME DESTINATION bin) - install(DIRECTORY ${NANA_INCLUDE_DIR}/nana DESTINATION include) # in ${CMAKE_INSTALL_PREFIX}/include/nana - message("The Nana include files will be installed in ${CMAKE_INSTALL_PREFIX}/include") -else() - # this is the prefered method to consume nana with cmake - target_sources(nana PUBLIC ${HEADERS}) - target_include_directories(nana PUBLIC ${NANA_INCLUDE_DIR}) -endif() - - -if(BUILD_SHARED_LIBS) # ?? - include(build/cmake/shared_libs.cmake) -endif() +option(NANA_CMAKE_ENABLE_MINGW_STD_THREADS_WITH_MEGANZ "replaced boost.thread with meganz's mingw-std-threads." OFF) # deprecate? ######## Nana options @@ -146,160 +129,42 @@ if(NANA_CMAKE_AUTOMATIC_GUI_TESTING) # todo: enable_testing() # ?? endif() -########### OS -# https://blog.kowalczyk.info/article/j/guide-to-predefined-macros-in-c-compilers-gcc-clang-msvc-etc..html -# http://nadeausoftware.com/articles/2012/01/c_c_tip_how_use_compiler_predefined_macros_detect_operating_system +list (APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/build/cmake/Modules) +include(build/cmake/install_nana.cmake) # includes and libs, or just expose the nana target +include(build/cmake/OS.cmake) # windows, unix, linux, apple, ... +include(build/cmake/shared_libs.cmake) # static and shared +include(build/cmake/compilers.cmake) # VC, gcc, clang -if(WIN32) - target_compile_definitions(nana PUBLIC WIN32) # todo: why not simple test for _WIN32 in code?? - set(CMAKE_DEBUG_POSTFIX "_d") - # Global MSVC definitions. You may prefer the hand-tuned sln and projects from the nana repository. - if(MSVC) - option(MSVC_USE_MP "Set to ON to build nana with the /MP option (Visual Studio 2005 and above)." ON) - option(MSVC_USE_STATIC_RUNTIME "Set to ON to build nana with the /MT(d) option." ON) - - # Change the MSVC Compiler flags - if(MSVC_USE_MP) - target_compile_options(nana PUBLIC "/MP" ) - endif() - - if(MSVC_USE_STATIC_RUNTIME) - foreach(flag - CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE - CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO - CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE - CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) - if(${flag} MATCHES "/MD") - string(REGEX REPLACE "/MD" "/MT" ${flag} "${${flag}}") - endif() - endforeach() - endif() - endif() - - if(MINGW) - if(NANA_CMAKE_ENABLE_MINGW_STD_THREADS_WITH_MEGANZ) # deprecated ????? - target_compile_definitions(nana PUBLIC STD_THREAD_NOT_SUPPORTED) - target_compile_definitions(nana PUBLIC NANA_ENABLE_MINGW_STD_THREADS_WITH_MEGANZ) - endif() - endif() -endif() - -if(APPLE) - target_compile_definitions(nana PUBLIC APPLE) # ??? not added by compilers? use __APPLE__ ? - target_include_directories(nana PUBLIC /opt/X11/include/) - target_link_libraries(nana PRIVATE iconv) - set(ENABLE_AUDIO OFF) -elseif(UNIX) - target_compile_definitions(nana PUBLIC linux) # todo: eliminate. Added by compilers. Also __linux posible -endif() - -if(UNIX) - find_package(X11 REQUIRED) - target_link_libraries(nana - PUBLIC ${X11_LIBRARIES} - PUBLIC ${X11_Xft_LIB} - ) - target_include_directories(nana SYSTEM - PUBLIC ${X11_Xft_INCLUDE_PATH} - PUBLIC ${X11_INCLUDE_DIR} - ) - find_package(Freetype) - if (FREETYPE_FOUND) - find_package(Fontconfig REQUIRED) - target_include_directories(nana SYSTEM - PUBLIC ${FREETYPE_INCLUDE_DIRS} - PUBLIC ${FONTCONFIG_INCLUDE_DIR} - ) - target_link_libraries(nana - PUBLIC ${FREETYPE_LIBRARIES} - PUBLIC ${FONTCONFIG_LIBRARIES} - ) - endif(FREETYPE_FOUND) -endif(UNIX) - - -########### Compilers -# -# Using gcc: gcc 4.8 don't support C++14 and make_unique. You may want to update at least to 4.9. -# gcc 5.3 and 5.4 include filesytem, but you need to add the link flag: -lstdc++fs -# -# In Windows, with CLion Allways check in File/Settings.../toolchains -# You could install MinGW-w64 from the TDM-GCC Compiler Suite for Windows which will update you to gcc 5.1. -# It is posible to follow https://computingabdn.com/softech/mingw-howto-install-gcc-for-windows/ -# and install MinGW with gcc 7.1 with has STD_THREADS and fs, from: https://sourceforge.net/projects/mingw-w64/files/ -# -# see at end of: https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_dynamic_or_shared.html -# -if(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") - - target_compile_options(nana PRIVATE -Wall) - if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") - if("${CMAKE_SYSTEM_NAME}" MATCHES "FreeBSD") - target_compile_options(nana PUBLIC -I/usr/local/include) - endif() - endif() -# set(THREADS_PREFER_PTHREAD_FLAG ON) -# find_package(Threads REQUIRED) -# target_link_libraries(${PROJECT_NAME} PRIVATE Threads::Threads) - - if(BUILD_SHARED_LIBS) - target_compile_options(nana PUBLIC -lgcc -lstdc++) - else() - - if(MINGW) - target_compile_options(nana PUBLIC -static) # -static ?? cmake knows BUILD_SHARED_LIBS - else() - target_compile_options(nana PUBLIC -static-libgcc -static-libstdc++) - endif() - endif(BUILD_SHARED_LIBS) - # target_link_libraries(nana PRIVATE stdc++fs) # ?? - - target_compile_options(nana PUBLIC -pthread) - -endif() - - -if (APPLE AND "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") # APPLE Clang - target_compile_options(nana PUBLIC -stdlib=libstdc++) -endif () - - -############# Optional libraries +############# Optional libraries ##################### include(build/cmake/enable_png.cmake) include(build/cmake/enable_jpeg.cmake) include(build/cmake/enable_audio.cmake) include(build/cmake/select_filesystem.cmake) -if("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") - target_compile_options(nana PRIVATE -fmax-errors=3) -endif() - - - -target_compile_features(${PROJECT_NAME} - PUBLIC cxx_nullptr - PUBLIC cxx_range_for - PUBLIC cxx_lambdas - PUBLIC cxx_decltype_auto - PUBLIC cxx_defaulted_functions - PUBLIC cxx_deleted_functions - PUBLIC cxx_auto_type - PUBLIC cxx_decltype_incomplete_return_types - PUBLIC cxx_defaulted_move_initializers - PUBLIC cxx_noexcept - PUBLIC cxx_rvalue_references -) - -# Just for information: +### Just for information: ######################################## include(CMakePrintHelpers) +# see: https://cmake.org/cmake/help/v3.12/manual/cmake-properties.7.html#properties-on-targets +cmake_print_properties(TARGETS nana PROPERTIES + COMPILE_DEFINITIONS COMPILE_OPTIONS COMPILE_FLAGS LINK_LIBRARIES + INCLUDE_DIRECTORIES INSTALL_NAME_DIR LINK_FLAGS VERSION + ) -message ("") + #message ("") # cmake_print_variables(SOURCES) cmake_print_variables(HEADERS) cmake_print_variables(PUBLIC_HEADERS) cmake_print_variables(NANA_CMAKE_INSTALL) +cmake_print_variables(Boost_INCLUDE_DIR) +cmake_print_variables(Boost_LIBRARIES) +cmake_print_variables(Boost::filesystem) + +cmake_print_variables(PNG_INCLUDE_DIRS) +cmake_print_variables(PNG_LIBRARIES) +cmake_print_variables(PNG::PNG) + + 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}) @@ -307,14 +172,6 @@ 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}) - -# see: https://cmake.org/cmake/help/v3.12/manual/cmake-properties.7.html#properties-on-targets -cmake_print_properties(TARGETS nana PROPERTIES - COMPILE_DEFINITIONS COMPILE_OPTIONS COMPILE_FLAGS LINK_LIBRARIES - INCLUDE_DIRECTORIES INSTALL_NAME_DIR LINK_FLAGS VERSION - ) - - message ( "DESTDIR = " ${DESTDIR}) message ( "CMAKE_INSTALL_PREFIX = " ${CMAKE_INSTALL_PREFIX}) message ( "NANA_INCLUDE_DIR = " ${NANA_INCLUDE_DIR}) diff --git a/build/cmake/OS.cmake b/build/cmake/OS.cmake new file mode 100644 index 00000000..022e1e83 --- /dev/null +++ b/build/cmake/OS.cmake @@ -0,0 +1,70 @@ +########### OS +# https://blog.kowalczyk.info/article/j/guide-to-predefined-macros-in-c-compilers-gcc-clang-msvc-etc..html +# http://nadeausoftware.com/articles/2012/01/c_c_tip_how_use_compiler_predefined_macros_detect_operating_system + +if(WIN32) + target_compile_definitions(nana PUBLIC WIN32) # todo: why not simple test for _WIN32 in code?? + set(CMAKE_DEBUG_POSTFIX "_d") # ?? + # Global MSVC definitions. You may prefer the hand-tuned sln and projects from the nana repository. + if(MSVC) + option(MSVC_USE_MP "Set to ON to build nana with the /MP option (Visual Studio 2005 and above)." ON) + option(MSVC_USE_STATIC_RUNTIME "Set to ON to build nana with the /MT(d) option." ON) + + # Change the MSVC Compiler flags + if(MSVC_USE_MP) + target_compile_options(nana PUBLIC "/MP" ) + endif() + + if(MSVC_USE_STATIC_RUNTIME) + foreach(flag + CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE + CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO + CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) + if(${flag} MATCHES "/MD") + string(REGEX REPLACE "/MD" "/MT" ${flag} "${${flag}}") + endif() + endforeach() + endif() + endif() + + if(MINGW) + if(NANA_CMAKE_ENABLE_MINGW_STD_THREADS_WITH_MEGANZ) # deprecated ????? + target_compile_definitions(nana PUBLIC STD_THREAD_NOT_SUPPORTED + PUBLIC NANA_ENABLE_MINGW_STD_THREADS_WITH_MEGANZ ) + endif() + endif() +endif() + +if(APPLE) + target_compile_definitions(nana PUBLIC APPLE) # ??? not added by compilers? use __APPLE__ ? + target_include_directories(nana PUBLIC /opt/X11/include/) + target_link_libraries(nana PRIVATE iconv) + set(ENABLE_AUDIO OFF) +endif() + +if(UNIX) + + find_package(X11 REQUIRED) # X11 - todo test PRIVATE + target_link_libraries(nana + PUBLIC ${X11_LIBRARIES} + PUBLIC ${X11_Xft_LIB} + ) + target_include_directories(nana SYSTEM + PUBLIC ${X11_Xft_INCLUDE_PATH} + PUBLIC ${X11_INCLUDE_DIR} + ) + + find_package(Freetype) # Freetype - todo test PRIVATE + if (FREETYPE_FOUND) + find_package(Fontconfig REQUIRED) + target_include_directories(nana SYSTEM + PUBLIC ${FREETYPE_INCLUDE_DIRS} + PUBLIC ${FONTCONFIG_INCLUDE_DIR} + ) + target_link_libraries(nana + PUBLIC ${FREETYPE_LIBRARIES} + PUBLIC ${FONTCONFIG_LIBRARIES} + ) + endif(FREETYPE_FOUND) +endif(UNIX) diff --git a/build/cmake/compilers.cmake b/build/cmake/compilers.cmake new file mode 100644 index 00000000..8cc53765 --- /dev/null +++ b/build/cmake/compilers.cmake @@ -0,0 +1,48 @@ + +########### Compilers +# +# Using gcc: gcc 4.8 don't support C++14 and make_unique. You may want to update at least to 4.9. +# gcc 5.3 and 5.4 include filesytem, but you need to add the link flag: -lstdc++fs +# +# In Windows, with CLion Allways check in File/Settings.../toolchains +# You could install MinGW-w64 from the TDM-GCC Compiler Suite for Windows which will update you to gcc 5.1. +# It is posible to follow https://computingabdn.com/softech/mingw-howto-install-gcc-for-windows/ +# and install MinGW with gcc 7.1 with has STD_THREADS and fs, from: https://sourceforge.net/projects/mingw-w64/files/ +# +# see at end of: https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_dynamic_or_shared.html +# + +if(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") # AND NOT MINGW?? + + target_compile_options(nana PRIVATE -Wall + PUBLIC -g ) + + set(THREADS_PREFER_PTHREAD_FLAG ON) # todo - test this + find_package(Threads REQUIRED) + target_link_libraries(nana PRIVATE Threads::Threads) + # target_compile_options(nana PUBLIC -pthread) + + + if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") + if("${CMAKE_SYSTEM_NAME}" MATCHES "FreeBSD") + target_compile_options(nana PUBLIC -I/usr/local/include) + endif() + endif() + + + # target_link_libraries(nana PRIVATE stdc++fs) # ?? + + +endif() + + +if (APPLE AND "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") # APPLE Clang + target_compile_options(nana PUBLIC -stdlib=libstdc++) +endif () + + + +if("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") + target_compile_options(nana PRIVATE -fmax-errors=3) +endif() + diff --git a/build/cmake/enable_audio.cmake b/build/cmake/enable_audio.cmake index 881b4c24..77ca7a4f 100644 --- a/build/cmake/enable_audio.cmake +++ b/build/cmake/enable_audio.cmake @@ -4,10 +4,10 @@ option(NANA_CMAKE_ENABLE_AUDIO "Enable class audio::play for PCM playback." OFF) if(NANA_CMAKE_ENABLE_AUDIO) target_compile_definitions(nana PUBLIC NANA_ENABLE_AUDIO) if(UNIX) - find_package(ASOUND) + find_package(ASOUND) # ? https://github.com/hintjens/demidi/blob/master/Findasound.cmake if(ASOUND_FOUND) target_include_directories(nana PUBLIC ${ASOUND_INCLUDE_DIRS}) - target_link_libraries(nana PUBLIC ${ASOUND_LIBRARY}) + target_link_libraries(nana PUBLIC ${ASOUND_LIBRARIES}) else() message(FATAL_ERROR "libasound is not found") endif() diff --git a/build/cmake/enable_jpeg.cmake b/build/cmake/enable_jpeg.cmake index a6956f66..008df7dc 100644 --- a/build/cmake/enable_jpeg.cmake +++ b/build/cmake/enable_jpeg.cmake @@ -9,9 +9,9 @@ if(NANA_CMAKE_ENABLE_JPEG) if(NANA_CMAKE_LIBJPEG_FROM_OS) find_package(JPEG) if(JPEG_FOUND) - target_include_directories(nana PUBLIC ${JPEG_INCLUDE_DIR}) - target_link_libraries (nana PUBLIC ${JPEG_LIBRARY}) - target_compile_definitions(nana PUBLIC USE_LIBJPEG_FROM_OS) + target_include_directories(nana PUBLIC ${JPEG_INCLUDE_DIRS}) + target_link_libraries (nana PUBLIC ${JPEG_LIBRARIES}) + target_compile_definitions(nana PUBLIC USE_LIBJPEG_FROM_OS) endif() else() target_compile_definitions(nana PUBLIC -ljpeg) diff --git a/build/cmake/enable_png.cmake b/build/cmake/enable_png.cmake index e60bd02e..7d3a5134 100644 --- a/build/cmake/enable_png.cmake +++ b/build/cmake/enable_png.cmake @@ -11,8 +11,10 @@ if(NANA_CMAKE_ENABLE_PNG) target_include_directories(nana PUBLIC ${PNG_INCLUDE_DIRS}) target_link_libraries (nana PUBLIC ${PNG_LIBRARIES}) target_compile_definitions(nana PUBLIC USE_LIBPNG_FROM_OS ${PNG_DEFINITIONS}) + # target_include_directories (nana SYSTEM PUBLIC PNG::PNG) # ?? + # target_compile_definitions (nana PUBLIC USE_LIBPNG_FROM_OS) endif() else() - target_compile_definitions(nana PUBLIC -lpng) + target_link_libraries(nana PUBLIC png) # provided by nana? endif() endif() \ No newline at end of file diff --git a/build/cmake/install_nana.cmake b/build/cmake/install_nana.cmake new file mode 100644 index 00000000..2b6851d6 --- /dev/null +++ b/build/cmake/install_nana.cmake @@ -0,0 +1,23 @@ +option(NANA_CMAKE_INSTALL "Install nana when compile the library (to be consumed without cmake)" OFF) + +# Install the include directories too. +if(NANA_CMAKE_INSTALL) + # this is the prefered method to consume nana directly with some specific bulid system + # Is your responsability to ensure all compiler options are compatible with the compilation + # of the project linking to the nana lib here generated + target_sources(nana PRIVATE ${HEADERS}) + target_include_directories(nana PRIVATE ${NANA_INCLUDE_DIR}) + message("The compiled Nana library will be installed in ${CMAKE_INSTALL_PREFIX}/lib") + # Actually in DESTDIR/CMAKE_INSTALL_PREFIX/lib but in windows there is no DESTDIR/ part. + install(TARGETS nana + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin) + install(DIRECTORY ${NANA_INCLUDE_DIR}/nana DESTINATION include) # in ${CMAKE_INSTALL_PREFIX}/include/nana + message("The Nana include files will be installed in ${CMAKE_INSTALL_PREFIX}/include") +else() + # this is the prefered method to consume nana with cmake + target_sources(nana PUBLIC ${HEADERS}) + target_include_directories(nana PUBLIC ${NANA_INCLUDE_DIR}) +endif() + diff --git a/build/cmake/select_filesystem.cmake b/build/cmake/select_filesystem.cmake index 1b54f1e2..036cf050 100644 --- a/build/cmake/select_filesystem.cmake +++ b/build/cmake/select_filesystem.cmake @@ -21,27 +21,33 @@ if(NANA_CMAKE_NANA_FILESYSTEM_FORCE) elseif(NANA_CMAKE_STD_FILESYSTEM_FORCE) target_compile_definitions(nana PUBLIC STD_FILESYSTEM_FORCE) - target_compile_options (nana PUBLIC -lstdc++fs) - -elseif(NANA_CMAKE_FIND_BOOST_FILESYSTEM OR NANA_CMAKE_BOOST_FILESYSTEM_FORCE) - if(NANA_CMAKE_BOOST_FILESYSTEM_FORCE) - target_compile_definitions(nana PUBLIC BOOST_FILESYSTEM_FORCE) - else() - target_compile_options (nana PUBLIC -lstdc++fs) - endif() + target_link_libraries (nana PUBLIC stdc++fs) +elseif(NANA_CMAKE_BOOST_FILESYSTEM_FORCE) + target_compile_definitions(nana PUBLIC BOOST_FILESYSTEM_FORCE) # https://cmake.org/cmake/help/git-master/module/FindBoost.html # Implicit dependencies such as Boost::filesystem requiring Boost::system will be automatically detected and satisfied, # 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) + find_package(Boost REQUIRED COMPONENTS filesystem) if(Boost_FOUND) target_compile_definitions(nana PUBLIC BOOST_FILESYSTEM_AVAILABLE) target_include_directories(nana PUBLIC "${Boost_INCLUDE_DIR}") # ?? SYSTEM target_link_libraries (nana PUBLIC ${Boost_LIBRARIES}) + # target_link_libraries (nana PUBLIC Boost::Boost) endif() set(Boost_USE_STATIC_LIBS ON) set(Boost_USE_STATIC_RUNTIME ON) + +else() + # todo test for std (for now just force nana or boost if there no std) + target_link_libraries (nana PUBLIC stdc++fs) + + # todo if not test for boost + # if not add nana filesystem endif() + + + diff --git a/build/cmake/shared_libs.cmake b/build/cmake/shared_libs.cmake index 6d7d6190..0734b47a 100644 --- a/build/cmake/shared_libs.cmake +++ b/build/cmake/shared_libs.cmake @@ -1,3 +1,8 @@ + +option(BUILD_SHARED_LIBS "Compile nana as a shared library." OFF) + +if(BUILD_SHARED_LIBS) # todo test + if(WIN32) set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) if(MSVC) @@ -25,6 +30,22 @@ --output-lib "libnana.lib") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libnana.def" - "${CMAKE_CURRENT_BINARY_DIR}/libnana.lib" DESTINATION lib) + "${CMAKE_CURRENT_BINARY_DIR}/libnana.lib" DESTINATION lib) endif() - endif() \ No newline at end of file + endif() +endif() + +if(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") # AND NOT MINGW?? + + if(BUILD_SHARED_LIBS) + target_compile_options(nana PUBLIC -lgcc -lstdc++) + else() + + if(MINGW) + target_compile_options(nana PUBLIC -static) # -static ?? cmake knows BUILD_SHARED_LIBS + else() + target_compile_options(nana PUBLIC -static-libgcc -static-libstdc++) + endif() + endif(BUILD_SHARED_LIBS) + +endif() \ No newline at end of file diff --git a/cmake/Modules/FindFontconfig.cmake b/cmake/Modules/FindFontconfig.cmake deleted file mode 100644 index e6fa81d8..00000000 --- a/cmake/Modules/FindFontconfig.cmake +++ /dev/null @@ -1,69 +0,0 @@ -# - Try to find the Fontconfig -# Once done this will define -# -# FONTCONFIG_FOUND - system has Fontconfig -# FONTCONFIG_INCLUDE_DIR - The include directory to use for the fontconfig headers -# FONTCONFIG_LIBRARIES - Link these to use FONTCONFIG -# FONTCONFIG_DEFINITIONS - Compiler switches required for using FONTCONFIG - -# Copyright (c) 2006,2007 Laurent Montel, -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# 1. Redistributions of source code must retain the copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. The name of the author may not be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# - -if (FONTCONFIG_LIBRARIES AND FONTCONFIG_INCLUDE_DIR) - - # in cache already - set(FONTCONFIG_FOUND TRUE) - -else (FONTCONFIG_LIBRARIES AND FONTCONFIG_INCLUDE_DIR) - - if (NOT WIN32) - # use pkg-config to get the directories and then use these values - # in the FIND_PATH() and FIND_LIBRARY() calls - find_package(PkgConfig) - pkg_check_modules(PC_FONTCONFIG fontconfig) - - set(FONTCONFIG_DEFINITIONS ${PC_FONTCONFIG_CFLAGS_OTHER}) - endif (NOT WIN32) - - find_path(FONTCONFIG_INCLUDE_DIR fontconfig/fontconfig.h - PATHS - ${PC_FONTCONFIG_INCLUDEDIR} - ${PC_FONTCONFIG_INCLUDE_DIRS} - /usr/X11/include - ) - - find_library(FONTCONFIG_LIBRARIES NAMES fontconfig - PATHS - ${PC_FONTCONFIG_LIBDIR} - ${PC_FONTCONFIG_LIBRARY_DIRS} - ) - - include(FindPackageHandleStandardArgs) - FIND_PACKAGE_HANDLE_STANDARD_ARGS(Fontconfig DEFAULT_MSG FONTCONFIG_LIBRARIES FONTCONFIG_INCLUDE_DIR ) - - mark_as_advanced(FONTCONFIG_LIBRARIES FONTCONFIG_INCLUDE_DIR) - -endif (FONTCONFIG_LIBRARIES AND FONTCONFIG_INCLUDE_DIR) From 6355d9ac7eaa1e455a027543827d49fc47ae912e Mon Sep 17 00:00:00 2001 From: qPCR4vir Date: Tue, 2 Oct 2018 18:50:49 +0200 Subject: [PATCH 053/149] Fontconfig scripts from https://github.com/cnjinhao/nana/pull/278 --- build/cmake/Modules/FindFontconfig.cmake | 69 ++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 build/cmake/Modules/FindFontconfig.cmake diff --git a/build/cmake/Modules/FindFontconfig.cmake b/build/cmake/Modules/FindFontconfig.cmake new file mode 100644 index 00000000..e6fa81d8 --- /dev/null +++ b/build/cmake/Modules/FindFontconfig.cmake @@ -0,0 +1,69 @@ +# - Try to find the Fontconfig +# Once done this will define +# +# FONTCONFIG_FOUND - system has Fontconfig +# FONTCONFIG_INCLUDE_DIR - The include directory to use for the fontconfig headers +# FONTCONFIG_LIBRARIES - Link these to use FONTCONFIG +# FONTCONFIG_DEFINITIONS - Compiler switches required for using FONTCONFIG + +# Copyright (c) 2006,2007 Laurent Montel, +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +if (FONTCONFIG_LIBRARIES AND FONTCONFIG_INCLUDE_DIR) + + # in cache already + set(FONTCONFIG_FOUND TRUE) + +else (FONTCONFIG_LIBRARIES AND FONTCONFIG_INCLUDE_DIR) + + if (NOT WIN32) + # use pkg-config to get the directories and then use these values + # in the FIND_PATH() and FIND_LIBRARY() calls + find_package(PkgConfig) + pkg_check_modules(PC_FONTCONFIG fontconfig) + + set(FONTCONFIG_DEFINITIONS ${PC_FONTCONFIG_CFLAGS_OTHER}) + endif (NOT WIN32) + + find_path(FONTCONFIG_INCLUDE_DIR fontconfig/fontconfig.h + PATHS + ${PC_FONTCONFIG_INCLUDEDIR} + ${PC_FONTCONFIG_INCLUDE_DIRS} + /usr/X11/include + ) + + find_library(FONTCONFIG_LIBRARIES NAMES fontconfig + PATHS + ${PC_FONTCONFIG_LIBDIR} + ${PC_FONTCONFIG_LIBRARY_DIRS} + ) + + include(FindPackageHandleStandardArgs) + FIND_PACKAGE_HANDLE_STANDARD_ARGS(Fontconfig DEFAULT_MSG FONTCONFIG_LIBRARIES FONTCONFIG_INCLUDE_DIR ) + + mark_as_advanced(FONTCONFIG_LIBRARIES FONTCONFIG_INCLUDE_DIR) + +endif (FONTCONFIG_LIBRARIES AND FONTCONFIG_INCLUDE_DIR) From 95ccc3cb7ef3c6152168aeb5af0aee196df80397 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Wed, 3 Oct 2018 08:05:38 +0800 Subject: [PATCH 054/149] fix bug that incorrect work path of test programs --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 743efe3b..d98c0dcf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -65,7 +65,7 @@ script: - make install # todo: separate resources from sources (a directory for images) - ls - - cd ../bin + - cd ../nana-test/bin - ls - ./a_group_impl - ./animate-bmp From 5213c2e8b92177e5e99823b85ad43f435837df49 Mon Sep 17 00:00:00 2001 From: qPCR4vir Date: Thu, 4 Oct 2018 10:14:58 +0200 Subject: [PATCH 055/149] reuse NANA_CMAKE_VERBOSE_PREPROCESSOR --- CMakeLists.txt | 84 ++++++++++++++++++++++++++------------------------ 1 file changed, 44 insertions(+), 40 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e427ee6..c6017cfe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -142,50 +142,54 @@ include(build/cmake/enable_audio.cmake) include(build/cmake/select_filesystem.cmake) -### Just for information: ######################################## -include(CMakePrintHelpers) -# see: https://cmake.org/cmake/help/v3.12/manual/cmake-properties.7.html#properties-on-targets -cmake_print_properties(TARGETS nana PROPERTIES - COMPILE_DEFINITIONS COMPILE_OPTIONS COMPILE_FLAGS LINK_LIBRARIES - INCLUDE_DIRECTORIES INSTALL_NAME_DIR LINK_FLAGS VERSION - ) +if (NANA_CMAKE_VERBOSE_PREPROCESSOR) - #message ("") -# cmake_print_variables(SOURCES) -cmake_print_variables(HEADERS) -cmake_print_variables(PUBLIC_HEADERS) -cmake_print_variables(NANA_CMAKE_INSTALL) + ### Just for information: ######################################## + include(CMakePrintHelpers) + # see: https://cmake.org/cmake/help/v3.12/manual/cmake-properties.7.html#properties-on-targets + cmake_print_properties(TARGETS nana PROPERTIES + COMPILE_DEFINITIONS COMPILE_OPTIONS COMPILE_FLAGS LINK_LIBRARIES + INCLUDE_DIRECTORIES INSTALL_NAME_DIR LINK_FLAGS VERSION + ) -cmake_print_variables(Boost_INCLUDE_DIR) -cmake_print_variables(Boost_LIBRARIES) -cmake_print_variables(Boost::filesystem) + #message ("") + # cmake_print_variables(SOURCES) + cmake_print_variables(HEADERS) + cmake_print_variables(PUBLIC_HEADERS) + cmake_print_variables(NANA_CMAKE_INSTALL) -cmake_print_variables(PNG_INCLUDE_DIRS) -cmake_print_variables(PNG_LIBRARIES) -cmake_print_variables(PNG::PNG) + cmake_print_variables(Boost_INCLUDE_DIR) + cmake_print_variables(Boost_LIBRARIES) + cmake_print_variables(Boost::filesystem) + + cmake_print_variables(PNG_INCLUDE_DIRS) + cmake_print_variables(PNG_LIBRARIES) + cmake_print_variables(PNG::PNG) -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 ( "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 ( "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 ( "CMAKE_CXX_COMPILER_VERSION = " ${CMAKE_CXX_COMPILER_VERSION}) + 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 ( "CMAKE_CXX_COMPILER_VERSION = " ${CMAKE_CXX_COMPILER_VERSION}) -message ( "NANA_CMAKE_NANA_FILESYSTEM_FORCE = " ${NANA_CMAKE_NANA_FILESYSTEM_FORCE}) -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_NANA_FILESYSTEM_FORCE = " ${NANA_CMAKE_NANA_FILESYSTEM_FORCE}) + 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}) + +endif() \ No newline at end of file From 905c58362166dfa4ef6ad9ba304165e963034b99 Mon Sep 17 00:00:00 2001 From: besh81 Date: Fri, 5 Oct 2018 11:45:09 +0200 Subject: [PATCH 056/149] fix folderbox init_path fix folderbox init_path --- source/gui/filebox.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/gui/filebox.cpp b/source/gui/filebox.cpp index fcaba515..7feda8bd 100644 --- a/source/gui/filebox.cpp +++ b/source/gui/filebox.cpp @@ -1257,7 +1257,7 @@ namespace nana IShellItem *init_path{ nullptr }; hr = SHCreateItemFromParsingName(impl_->init_path.wstring().c_str(), nullptr, IID_PPV_ARGS(&init_path)); if (SUCCEEDED(hr)) - fd->SetDefaultFolder(init_path); + fd->SetFolder(init_path); fd->SetOptions(FOS_PICKFOLDERS); fd->Show(reinterpret_cast(API::root(impl_->owner))); // the native handle of the parent nana form goes here From 3c672c4a53c6b90ce41c617c53430d404c6c86d8 Mon Sep 17 00:00:00 2001 From: qPCR4vir Date: Fri, 5 Oct 2018 12:06:06 +0200 Subject: [PATCH 057/149] fix to full path ? CLion ? CMake ? me ? have problem with relative path? --- CMakeLists.txt | 16 ++++++++-------- build/cmake/select_filesystem.cmake | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c6017cfe..080b2952 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -130,16 +130,16 @@ if(NANA_CMAKE_AUTOMATIC_GUI_TESTING) endif() list (APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/build/cmake/Modules) -include(build/cmake/install_nana.cmake) # includes and libs, or just expose the nana target -include(build/cmake/OS.cmake) # windows, unix, linux, apple, ... -include(build/cmake/shared_libs.cmake) # static and shared -include(build/cmake/compilers.cmake) # VC, gcc, clang +include(${CMAKE_CURRENT_LIST_DIR}/build/cmake/install_nana.cmake) # includes and libs, or just expose the nana target +include(${CMAKE_CURRENT_LIST_DIR}/build/cmake/OS.cmake) # windows, unix, linux, apple, ... +include(${CMAKE_CURRENT_LIST_DIR}/build/cmake/shared_libs.cmake) # static and shared +include(${CMAKE_CURRENT_LIST_DIR}/build/cmake/compilers.cmake) # VC, gcc, clang ############# Optional libraries ##################### -include(build/cmake/enable_png.cmake) -include(build/cmake/enable_jpeg.cmake) -include(build/cmake/enable_audio.cmake) -include(build/cmake/select_filesystem.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/build/cmake/enable_png.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/build/cmake/enable_jpeg.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/build/cmake/enable_audio.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/build/cmake/select_filesystem.cmake) if (NANA_CMAKE_VERBOSE_PREPROCESSOR) diff --git a/build/cmake/select_filesystem.cmake b/build/cmake/select_filesystem.cmake index 036cf050..974ae7d1 100644 --- a/build/cmake/select_filesystem.cmake +++ b/build/cmake/select_filesystem.cmake @@ -11,10 +11,10 @@ # the previus were selected or available. # You can change that default if you change one of the following # (please don't define more than one of the _XX_FORCE options): -option(NANA_CMAKE_FIND_BOOST_FILESYSTEM "Search: Is Boost filesystem available?" OFF) option(NANA_CMAKE_NANA_FILESYSTEM_FORCE "Force nana filesystem over ISO and boost?" OFF) option(NANA_CMAKE_STD_FILESYSTEM_FORCE "Use of STD filesystem?(a compilation error will ocurre if not available)" OFF) option(NANA_CMAKE_BOOST_FILESYSTEM_FORCE "Force use of Boost filesystem if available (over STD)?" OFF) +option(NANA_CMAKE_FIND_BOOST_FILESYSTEM "Search: Is Boost filesystem available?" OFF) if(NANA_CMAKE_NANA_FILESYSTEM_FORCE) target_compile_definitions(nana PUBLIC NANA_FILESYSTEM_FORCE) From 9792b1ed2fe3b6b3c26f0e4097b6208922706490 Mon Sep 17 00:00:00 2001 From: qPCR4vir Date: Sat, 6 Oct 2018 20:31:58 +0200 Subject: [PATCH 058/149] verbose.cmake # Just for information --- CMakeLists.txt | 60 +---------------------------- build/cmake/select_filesystem.cmake | 6 ++- build/cmake/verbose.cmake | 57 +++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 60 deletions(-) create mode 100644 build/cmake/verbose.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 080b2952..896e4134 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -113,17 +113,12 @@ foreach(subdir ${NANA_INCLUDE_SUBDIRS}) endforeach() ### Some nana compilation options ### -option(NANA_CMAKE_VERBOSE_PREPROCESSOR "Show annoying debug messages during compilation." OFF) -option(NANA_CMAKE_STOP_VERBOSE_PREPROCESSOR "Stop compilation after showing the annoying debug messages." OFF) option(NANA_CMAKE_AUTOMATIC_GUI_TESTING "Activate automatic GUI testing?" OFF) option(NANA_CMAKE_ENABLE_MINGW_STD_THREADS_WITH_MEGANZ "replaced boost.thread with meganz's mingw-std-threads." OFF) # deprecate? ######## Nana options target_compile_definitions(nana PRIVATE NANA_IGNORE_CONF) # really ? -if(NANA_CMAKE_VERBOSE_PREPROCESSOR) - target_compile_definitions(nana PRIVATE VERBOSE_PREPROCESSOR) -endif() if(NANA_CMAKE_AUTOMATIC_GUI_TESTING) target_compile_definitions(nana PUBLIC NANA_AUTOMATIC_GUI_TESTING) # todo: enable_testing() # ?? @@ -132,7 +127,7 @@ endif() list (APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/build/cmake/Modules) include(${CMAKE_CURRENT_LIST_DIR}/build/cmake/install_nana.cmake) # includes and libs, or just expose the nana target include(${CMAKE_CURRENT_LIST_DIR}/build/cmake/OS.cmake) # windows, unix, linux, apple, ... -include(${CMAKE_CURRENT_LIST_DIR}/build/cmake/shared_libs.cmake) # static and shared +include(${CMAKE_CURRENT_LIST_DIR}/build/cmake/shared_libs.cmake) # static vs shared include(${CMAKE_CURRENT_LIST_DIR}/build/cmake/compilers.cmake) # VC, gcc, clang ############# Optional libraries ##################### @@ -140,56 +135,5 @@ include(${CMAKE_CURRENT_LIST_DIR}/build/cmake/enable_png.cmake) include(${CMAKE_CURRENT_LIST_DIR}/build/cmake/enable_jpeg.cmake) include(${CMAKE_CURRENT_LIST_DIR}/build/cmake/enable_audio.cmake) include(${CMAKE_CURRENT_LIST_DIR}/build/cmake/select_filesystem.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/build/cmake/verbose.cmake) # Just for information - -if (NANA_CMAKE_VERBOSE_PREPROCESSOR) - - ### Just for information: ######################################## - include(CMakePrintHelpers) - # see: https://cmake.org/cmake/help/v3.12/manual/cmake-properties.7.html#properties-on-targets - cmake_print_properties(TARGETS nana PROPERTIES - COMPILE_DEFINITIONS COMPILE_OPTIONS COMPILE_FLAGS LINK_LIBRARIES - INCLUDE_DIRECTORIES INSTALL_NAME_DIR LINK_FLAGS VERSION - ) - - #message ("") - # cmake_print_variables(SOURCES) - cmake_print_variables(HEADERS) - cmake_print_variables(PUBLIC_HEADERS) - cmake_print_variables(NANA_CMAKE_INSTALL) - - cmake_print_variables(Boost_INCLUDE_DIR) - cmake_print_variables(Boost_LIBRARIES) - cmake_print_variables(Boost::filesystem) - - cmake_print_variables(PNG_INCLUDE_DIRS) - cmake_print_variables(PNG_LIBRARIES) - cmake_print_variables(PNG::PNG) - - - 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 ( "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 ( "CMAKE_CXX_COMPILER_VERSION = " ${CMAKE_CXX_COMPILER_VERSION}) - - message ( "NANA_CMAKE_NANA_FILESYSTEM_FORCE = " ${NANA_CMAKE_NANA_FILESYSTEM_FORCE}) - 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}) - -endif() \ No newline at end of file diff --git a/build/cmake/select_filesystem.cmake b/build/cmake/select_filesystem.cmake index 974ae7d1..10c9836f 100644 --- a/build/cmake/select_filesystem.cmake +++ b/build/cmake/select_filesystem.cmake @@ -32,9 +32,11 @@ elseif(NANA_CMAKE_BOOST_FILESYSTEM_FORCE) find_package(Boost REQUIRED COMPONENTS filesystem) if(Boost_FOUND) target_compile_definitions(nana PUBLIC BOOST_FILESYSTEM_AVAILABLE) - target_include_directories(nana PUBLIC "${Boost_INCLUDE_DIR}") # ?? SYSTEM + # SYSTEM - ignore warnings from here + target_include_directories(nana SYSTEM PUBLIC "${Boost_INCLUDE_DIR}") # ?? SYSTEM target_link_libraries (nana PUBLIC ${Boost_LIBRARIES}) - # target_link_libraries (nana PUBLIC Boost::Boost) + # target_include_directories (nana SYSTEM PUBLIC Boost::Boost) + # message("boost found true") endif() set(Boost_USE_STATIC_LIBS ON) set(Boost_USE_STATIC_RUNTIME ON) diff --git a/build/cmake/verbose.cmake b/build/cmake/verbose.cmake new file mode 100644 index 00000000..6d95eb9e --- /dev/null +++ b/build/cmake/verbose.cmake @@ -0,0 +1,57 @@ +option(NANA_CMAKE_VERBOSE_PREPROCESSOR "Show annoying debug messages during compilation." OFF) +option(NANA_CMAKE_STOP_VERBOSE_PREPROCESSOR "Stop compilation after showing the annoying debug messages." OFF) + + +if (NANA_CMAKE_VERBOSE_PREPROCESSOR) + + target_compile_definitions(nana PRIVATE VERBOSE_PREPROCESSOR) + + ### Just for information: ######################################## + include(CMakePrintHelpers) + # see: https://cmake.org/cmake/help/v3.12/manual/cmake-properties.7.html#properties-on-targets + cmake_print_properties(TARGETS nana PROPERTIES + COMPILE_DEFINITIONS COMPILE_OPTIONS COMPILE_FLAGS LINK_LIBRARIES + INCLUDE_DIRECTORIES INSTALL_NAME_DIR LINK_FLAGS VERSION + ) + + #message ("") + # cmake_print_variables(SOURCES) + cmake_print_variables(HEADERS) + cmake_print_variables(PUBLIC_HEADERS) + cmake_print_variables(NANA_CMAKE_INSTALL) + + cmake_print_variables(Boost_INCLUDE_DIR) + cmake_print_variables(Boost_LIBRARIES) + cmake_print_variables(Boost::filesystem) + + cmake_print_variables(PNG_INCLUDE_DIRS) + cmake_print_variables(PNG_LIBRARIES) + cmake_print_variables(PNG::PNG) + + cmake_print_variables(CMAKE_BUILD_TYPE) + cmake_print_variables(CMAKE_CONFIGURATION_TYPES) + 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 ( "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 ( "CMAKE_MAKE_PROGRAM = " ${CMAKE_MAKE_PROGRAM}) + message ( "CMAKE_CXX_COMPILER_VERSION = " ${CMAKE_CXX_COMPILER_VERSION}) + + message ( "NANA_CMAKE_NANA_FILESYSTEM_FORCE = " ${NANA_CMAKE_NANA_FILESYSTEM_FORCE}) + 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}) + +endif(NANA_CMAKE_VERBOSE_PREPROCESSOR) \ No newline at end of file From 943a9e444d757dbdf868c4ff607bbccfb21f5485 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Tue, 9 Oct 2018 03:56:50 +0800 Subject: [PATCH 059/149] init dragdrop contains a new class simple_dragdrop and changes of listbox for dragdrop --- build/codeblocks/nana.cbp | 1 + build/vc2013/nana.vcxproj | 1 + build/vc2013/nana.vcxproj.filters | 3 + build/vc2015/nana.vcxproj | 1 + build/vc2015/nana.vcxproj.filters | 3 + build/vc2017/nana.vcxproj | 1 + build/vc2017/nana.vcxproj.filters | 3 + include/nana/gui/basis.hpp | 7 + include/nana/gui/detail/basic_window.hpp | 6 +- include/nana/gui/dragdrop.hpp | 45 ++ include/nana/gui/programming_interface.hpp | 7 +- include/nana/gui/widgets/listbox.hpp | 3 + source/gui/detail/basic_window.cpp | 3 +- source/gui/dragdrop.cpp | 609 ++++++++++++++++++ source/gui/programming_interface.cpp | 31 + source/gui/widgets/listbox.cpp | 92 ++- source/gui/widgets/skeletons/content_view.cpp | 15 +- 17 files changed, 812 insertions(+), 19 deletions(-) create mode 100644 include/nana/gui/dragdrop.hpp create mode 100644 source/gui/dragdrop.cpp diff --git a/build/codeblocks/nana.cbp b/build/codeblocks/nana.cbp index 18c78755..d48270ec 100644 --- a/build/codeblocks/nana.cbp +++ b/build/codeblocks/nana.cbp @@ -66,6 +66,7 @@ + diff --git a/build/vc2013/nana.vcxproj b/build/vc2013/nana.vcxproj index 8501f83f..4051495b 100644 --- a/build/vc2013/nana.vcxproj +++ b/build/vc2013/nana.vcxproj @@ -202,6 +202,7 @@ + diff --git a/build/vc2013/nana.vcxproj.filters b/build/vc2013/nana.vcxproj.filters index 6fe722eb..4c14b4d7 100644 --- a/build/vc2013/nana.vcxproj.filters +++ b/build/vc2013/nana.vcxproj.filters @@ -333,6 +333,9 @@ Source Files\nana\detail + + Source Files\nana\gui + diff --git a/build/vc2015/nana.vcxproj b/build/vc2015/nana.vcxproj index 3ec4cf7d..f271092c 100644 --- a/build/vc2015/nana.vcxproj +++ b/build/vc2015/nana.vcxproj @@ -196,6 +196,7 @@ + diff --git a/build/vc2015/nana.vcxproj.filters b/build/vc2015/nana.vcxproj.filters index d1d7ba78..c0cfcbdc 100644 --- a/build/vc2015/nana.vcxproj.filters +++ b/build/vc2015/nana.vcxproj.filters @@ -291,6 +291,9 @@ Source Files\detail + + Source Files\gui + diff --git a/build/vc2017/nana.vcxproj b/build/vc2017/nana.vcxproj index cc3c70dc..39d61fe8 100644 --- a/build/vc2017/nana.vcxproj +++ b/build/vc2017/nana.vcxproj @@ -179,6 +179,7 @@ + diff --git a/build/vc2017/nana.vcxproj.filters b/build/vc2017/nana.vcxproj.filters index 10e92180..65431a04 100644 --- a/build/vc2017/nana.vcxproj.filters +++ b/build/vc2017/nana.vcxproj.filters @@ -292,6 +292,9 @@ Sources\gui + + Sources\gui + diff --git a/include/nana/gui/basis.hpp b/include/nana/gui/basis.hpp index a582d7b9..f7ed845d 100644 --- a/include/nana/gui/basis.hpp +++ b/include/nana/gui/basis.hpp @@ -65,6 +65,13 @@ namespace nana blend }; + enum class dragdrop_status + { + not_ready, + ready, + in_progress + }; + namespace category { enum class flags diff --git a/include/nana/gui/detail/basic_window.hpp b/include/nana/gui/detail/basic_window.hpp index 6c5bc202..5911b598 100644 --- a/include/nana/gui/detail/basic_window.hpp +++ b/include/nana/gui/detail/basic_window.hpp @@ -1,7 +1,7 @@ /** * A Basic Window Widget Definition * 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 @@ -179,7 +179,8 @@ namespace detail bool ignore_menubar_focus : 1; ///< A flag indicates whether the menubar sets the focus. bool ignore_mouse_focus : 1; ///< A flag indicates whether the widget accepts focus when clicking on it bool space_click_enabled : 1; ///< A flag indicates whether enable mouse_down/click/mouse_up when pressing and releasing whitespace key. - unsigned Reserved :18; + bool draggable : 1; + unsigned Reserved :17; unsigned char tab; ///< indicate a window that can receive the keyboard TAB mouse_action action; mouse_action action_before; @@ -234,6 +235,7 @@ namespace detail ///< if the active_window is null, the parent of this window keeps focus. paint::graphics glass_buffer; ///< if effect.bground is avaiable. Refer to window_layout::make_bground. update_state upd_state; + dragdrop_status dnd_state{ dragdrop_status::not_ready }; union { diff --git a/include/nana/gui/dragdrop.hpp b/include/nana/gui/dragdrop.hpp new file mode 100644 index 00000000..e68279b8 --- /dev/null +++ b/include/nana/gui/dragdrop.hpp @@ -0,0 +1,45 @@ +/** +* Drag and Drop Implementation +* Nana C++ Library(http://www.nanapro.org) +* Copyright(C) 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/dragdrop.hpp +* @author: Jinhao(cnjinhao@hotmail.com) +*/ +#ifndef NANA_GUI_DRAGDROP_INCLUDED +#define NANA_GUI_DRAGDROP_INCLUDED + +#include +#include +#include "basis.hpp" + +#include + +namespace nana +{ + class simple_dragdrop + { + struct implementation; + + simple_dragdrop(const simple_dragdrop&) = delete; + simple_dragdrop& operator=(const simple_dragdrop&) = delete; + + simple_dragdrop(simple_dragdrop&&) = delete; + simple_dragdrop& operator=(simple_dragdrop&&) = delete; + public: + simple_dragdrop(window drag_wd); + ~simple_dragdrop(); + + /// Sets a condition that determines whether the drag&drop can start + void condition(std::function predicate_fn); + void make_drop(window target, std::function drop_fn); + private: + implementation* const impl_; + }; +} + +#endif \ No newline at end of file diff --git a/include/nana/gui/programming_interface.hpp b/include/nana/gui/programming_interface.hpp index 134b0082..56c3ec7d 100644 --- a/include/nana/gui/programming_interface.hpp +++ b/include/nana/gui/programming_interface.hpp @@ -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 @@ -117,6 +117,9 @@ namespace API void lazy_refresh(); void draw_shortkey_underline(paint::graphics&, const std::string& text, wchar_t shortkey, std::size_t shortkey_position, const point& text_pos, const color&); + + void window_draggable(window, bool enabled); + bool window_draggable(window); }//end namespace dev @@ -476,6 +479,8 @@ namespace API ::std::optional> content_extent(window wd, unsigned limited_px, bool limit_width); unsigned screen_dpi(bool x_requested); + + dragdrop_status window_dragdrop_status(::nana::window); }//end namespace API }//end namespace nana diff --git a/include/nana/gui/widgets/listbox.hpp b/include/nana/gui/widgets/listbox.hpp index a2eee239..dde46edc 100644 --- a/include/nana/gui/widgets/listbox.hpp +++ b/include/nana/gui/widgets/listbox.hpp @@ -1497,6 +1497,9 @@ the nana::detail::basic_window member pointer scheme void erase(index_pairs indexes); /// +#include + +#include +#include + +#include +#include + +#ifdef NANA_WINDOWS +# include +# include +# include +# include +#endif + +namespace nana +{ + /// drop_association + /** + * This class is used for querying whether tow windows have a connection of drag and drop + */ + class drop_association + { + public: + void add(window source, window target) + { + assoc_[source].insert(target); + } + + void erase(window wd) + { + assoc_.erase(wd); + + for (auto & assoc : assoc_) + assoc.second.erase(wd); + } + + bool has(window source, window target) const + { + auto i = assoc_.find(source); + if (i != assoc_.end()) + return (0 != i->second.count(target)); + + return false; + } + private: + std::map> assoc_; + }; + +#ifdef NANA_WINDOWS + template + class win32com_iunknown : public Interface + { + public: + //Implements IUnknown + STDMETHODIMP QueryInterface(REFIID riid, void **ppv) + { + if (riid == IID_IUnknown || riid == iid) { + *ppv = static_cast(this); + AddRef(); + return S_OK; + } + *ppv = NULL; + return E_NOINTERFACE; + } + + STDMETHODIMP_(ULONG) AddRef() + { + return InterlockedIncrement(&ref_count_); + } + + STDMETHODIMP_(ULONG) Release() + { + LONG cRef = InterlockedDecrement(&ref_count_); + if (cRef == 0) delete this; + return cRef; + } + private: + LONG ref_count_{ 1 }; + }; + + + class win32com_drop_target : public IDropTarget + { + public: + win32com_drop_target(const drop_association& drop_assoc) : + drop_assoc_(drop_assoc) + {} + + void set_source(window wd) + { + source_window_ = wd; + } + public: + //Implements IUnknown + STDMETHODIMP QueryInterface(REFIID riid, void **ppv) + { + if (riid == IID_IUnknown || riid == IID_IDropTarget) { + *ppv = static_cast(this); + AddRef(); + return S_OK; + } + *ppv = NULL; + return E_NOINTERFACE; + } + + STDMETHODIMP_(ULONG) AddRef() + { + return InterlockedIncrement(&ref_count_); + } + + STDMETHODIMP_(ULONG) Release() + { + LONG cRef = InterlockedDecrement(&ref_count_); + if (cRef == 0) delete this; + return cRef; + } + + private: + // IDropTarget + STDMETHODIMP DragEnter(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) + { + *pdwEffect &= DROPEFFECT_COPY; + return S_OK; + } + + STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) + { + auto hovered_wd = API::find_window(point(pt.x, pt.y)); + if ((hovered_wd && (hovered_wd == source_window_)) || drop_assoc_.has(source_window_, hovered_wd)) + *pdwEffect &= DROPEFFECT_COPY; + else + *pdwEffect = DROPEFFECT_NONE; + + return S_OK; + } + + STDMETHODIMP DragLeave() + { + return E_NOTIMPL; + } + + STDMETHODIMP Drop(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) + { + return E_NOTIMPL; + } + private: + LONG ref_count_{ 1 }; + + window source_window_{ nullptr }; + const drop_association& drop_assoc_; + }; + + class drop_source : public win32com_iunknown + { + public: + drop_source(window wd) : + window_handle_(wd) + {} + + window source() const + { + return window_handle_; + } + private: + // IDropSource + STDMETHODIMP QueryContinueDrag(BOOL esc_pressed, DWORD key_state) override + { + if (esc_pressed) + return DRAGDROP_S_CANCEL; + + //Drop the object if left button is released. + if (0 == (key_state & (MK_LBUTTON))) + return DRAGDROP_S_DROP; + + return S_OK; + } + + STDMETHODIMP GiveFeedback(DWORD effect) override + { + return DRAGDROP_S_USEDEFAULTCURSORS; + } + private: + window const window_handle_; + }; + + class drop_data : public win32com_iunknown + { + struct medium + { + STGMEDIUM * stgmedium; + FORMATETC * format; + }; + public: + STDMETHODIMP GetData(FORMATETC *request_format, STGMEDIUM *pmedium) override + { + if (!(request_format && pmedium)) + return E_INVALIDARG; + + pmedium->hGlobal = nullptr; + + for (auto & med : mediums_) + { + if ((request_format->tymed & med.format->tymed) && + (request_format->dwAspect == med.format->dwAspect) && + (request_format->cfFormat == med.format->cfFormat)) + { + return _m_copy_medium(pmedium, med.stgmedium, med.format); + } + } + + return DV_E_FORMATETC; + } + + STDMETHODIMP GetDataHere(FORMATETC *pformatetc, STGMEDIUM *pmedium) override + { + return E_NOTIMPL; + } + + STDMETHODIMP QueryGetData(FORMATETC *pformatetc) override + { + if (NULL == pformatetc) + return E_INVALIDARG; + + if (!(DVASPECT_CONTENT & pformatetc->dwAspect)) + return DV_E_DVASPECT; + + HRESULT result = DV_E_TYMED; + + for(auto & med : mediums_) + { + if (med.format->tymed & pformatetc->tymed) + { + if (med.format->cfFormat == pformatetc->cfFormat) + return S_OK; + + result = DV_E_FORMATETC; + } + } + return result; + } + + STDMETHODIMP GetCanonicalFormatEtc(FORMATETC *pformatectIn, FORMATETC *pformatetcOut) override + { + return E_NOTIMPL; + } + + STDMETHODIMP SetData(FORMATETC *pformatetc, STGMEDIUM *pmedium, BOOL fRelease) override + { + if (!(pformatetc && pmedium)) + return E_INVALIDARG; + + if (pformatetc->tymed != pmedium->tymed) + return E_FAIL; + + medium retain; + retain.format = new FORMATETC; + retain.stgmedium = new (std::nothrow) STGMEDIUM; + if (nullptr == retain.stgmedium) + { + delete retain.format; + return E_FAIL; + } + + std::memset(retain.format, 0, sizeof(FORMATETC)); + std::memset(retain.stgmedium, 0, sizeof(STGMEDIUM)); + + *retain.format = *pformatetc; + + _m_copy_medium(retain.stgmedium, pmedium, pformatetc); + + if (TRUE == fRelease) + ::ReleaseStgMedium(pmedium); + + mediums_.emplace_back(retain); + return S_OK; + } + + STDMETHODIMP EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC **ppenumFormatEtc) override + { + if (NULL == ppenumFormatEtc) + return E_INVALIDARG; + + if (DATADIR_GET != dwDirection) + return E_NOTIMPL; + + *ppenumFormatEtc = NULL; + + FORMATETC rgfmtetc[] = + { + { CF_UNICODETEXT, NULL, DVASPECT_CONTENT, 0, TYMED_HGLOBAL }, + }; + return ::SHCreateStdEnumFmtEtc(ARRAYSIZE(rgfmtetc), rgfmtetc, ppenumFormatEtc); + } + + STDMETHODIMP DAdvise(FORMATETC *pformatetc, DWORD advf, IAdviseSink *pAdvSink, DWORD *pdwConnection) override + { + return OLE_E_ADVISENOTSUPPORTED; + } + + STDMETHODIMP DUnadvise(DWORD dwConnection) override + { + return OLE_E_ADVISENOTSUPPORTED; + } + + STDMETHODIMP EnumDAdvise(IEnumSTATDATA **ppenumAdvise) override + { + return OLE_E_ADVISENOTSUPPORTED; + } + private: + static HRESULT _m_copy_medium(STGMEDIUM* stgmed_dst, STGMEDIUM* stgmed_src, FORMATETC* fmt_src) + { + if (!(stgmed_dst && stgmed_src && fmt_src)) + return E_INVALIDARG; + + switch (stgmed_src->tymed) + { + case TYMED_HGLOBAL: + stgmed_dst->hGlobal = (HGLOBAL)OleDuplicateData(stgmed_src->hGlobal, fmt_src->cfFormat, 0); + break; + case TYMED_GDI: + stgmed_dst->hBitmap = (HBITMAP)OleDuplicateData(stgmed_src->hBitmap, fmt_src->cfFormat, 0); + break; + case TYMED_MFPICT: + stgmed_dst->hMetaFilePict = (HMETAFILEPICT)OleDuplicateData(stgmed_src->hMetaFilePict, fmt_src->cfFormat, 0); + break; + case TYMED_ENHMF: + stgmed_dst->hEnhMetaFile = (HENHMETAFILE)OleDuplicateData(stgmed_src->hEnhMetaFile, fmt_src->cfFormat, 0); + break; + case TYMED_FILE: + stgmed_dst->lpszFileName = (LPOLESTR)OleDuplicateData(stgmed_src->lpszFileName, fmt_src->cfFormat, 0); + break; + case TYMED_ISTREAM: + stgmed_dst->pstm = stgmed_src->pstm; + stgmed_src->pstm->AddRef(); + break; + case TYMED_ISTORAGE: + stgmed_dst->pstg = stgmed_src->pstg; + stgmed_src->pstg->AddRef(); + break; + case TYMED_NULL: + default: + break; + } + stgmed_dst->tymed = stgmed_src->tymed; + stgmed_dst->pUnkForRelease = nullptr; + if (stgmed_src->pUnkForRelease) + { + stgmed_dst->pUnkForRelease = stgmed_src->pUnkForRelease; + stgmed_src->pUnkForRelease->AddRef(); + } + return S_OK; + } + private: + std::vector mediums_; + }; +#endif + + class dragdrop_service + { + dragdrop_service() = default; + public: + static dragdrop_service& instance() + { + static dragdrop_service serv; + return serv; + } + + void create_dragdrop(window wd) + { + auto native_wd = API::root(wd); + if (nullptr == native_wd) + return; + + if(table_.empty()) + ::OleInitialize(nullptr); + + win32com_drop_target* drop_target = nullptr; + + auto i = table_.find(native_wd); + if (i == table_.end()) + { + drop_target = new win32com_drop_target{drop_assoc_}; + ::RegisterDragDrop(reinterpret_cast(native_wd), drop_target); + + table_[native_wd] = drop_target; + } + else + { + drop_target = i->second; + drop_target->AddRef(); + } + } + + void remove(window wd) + { + auto i = table_.find(API::root(wd)); + if (i != table_.end()) + { + if (0 == i->second->Release()) + table_.erase(i); + } + + drop_assoc_.erase(wd); + } + + bool dragdrop(window drag_wd) + { + auto i = table_.find(API::root(drag_wd)); + if (table_.end() == i) + return false; + + auto drop_src = new drop_source{ drag_wd }; + auto drop_dat = new (std::nothrow) drop_data; + if (!drop_dat) + { + delete drop_src; + return false; + } + + i->second->set_source(drag_wd); + + + DWORD eff; + auto status = ::DoDragDrop(drop_dat, drop_src, DROPEFFECT_COPY, &eff); + + i->second->set_source(nullptr); + return true; + } + + drop_association& drop_assoc() + { + return drop_assoc_; + } + private: + std::map table_; + drop_association drop_assoc_; + }; + + + struct simple_dragdrop::implementation + { + window window_handle; + std::function predicate; + std::map> targets; + + bool dragging{ false }; + +#if 0 + bool cancel() + { + if (!dragging) + return false; + + if (API::is_window(window_handle)) + { + dragging = true; + using basic_window = ::nana::detail::basic_window; + auto real_wd = reinterpret_cast<::nana::detail::basic_window*>(window_handle); + real_wd->other.dnd_state = dragdrop_status::not_ready; + } + + API::release_capture(window_handle); + dragging = false; + + return true; + } +#endif + }; + + + + simple_dragdrop::simple_dragdrop(window drag_wd) : + impl_(new implementation) + { + dragdrop_service::instance().create_dragdrop(drag_wd); + + if (!API::is_window(drag_wd)) + { + delete impl_; + throw std::invalid_argument("simple_dragdrop: invalid window handle"); + } + + impl_->window_handle = drag_wd; + API::dev::window_draggable(drag_wd, true); + + auto & events = API::events<>(drag_wd); + +#ifdef NANA_WINDOWS + events.mouse_down.connect_unignorable([this](const arg_mouse& arg){ + if (arg.is_left_button() && API::is_window(impl_->window_handle)) + { + impl_->dragging = ((!impl_->predicate) || impl_->predicate()); + } + }); + + events.mouse_move.connect_unignorable([this](const arg_mouse& arg) { + if (!(arg.is_left_button() && impl_->dragging && API::is_window(arg.window_handle))) + return; + + using basic_window = ::nana::detail::basic_window; + auto real_wd = reinterpret_cast<::nana::detail::basic_window*>(arg.window_handle); + real_wd->other.dnd_state = dragdrop_status::in_progress; + + auto has_dropped = dragdrop_service::instance().dragdrop(arg.window_handle); + + real_wd->other.dnd_state = dragdrop_status::not_ready; + impl_->dragging = false; + + if (has_dropped) + { + auto drop_wd = API::find_window(API::cursor_position()); + auto i = impl_->targets.find(drop_wd); + if ((impl_->targets.end() != i) && i->second) + i->second(); + } + }); +#else + events.mouse_down.connect_unignorable([drag_wd](const arg_mouse& arg){ + if (arg.is_left_button() && API::is_window(drag_wd)) + { + API::set_capture(drag_wd, true); + + using basic_window = ::nana::detail::basic_window; + auto real_wd = reinterpret_cast<::nana::detail::basic_window*>(drag_wd); + real_wd->other.dnd_state = dragdrop_status::ready; + } + }); + + events.mouse_move.connect_unignorable([this](const arg_mouse& arg){ + if (!arg.is_left_button()) + return; + + if (impl_->dragging) + { + auto drop_wd = API::find_window(API::cursor_position()); + auto i = impl_->targets.find(drop_wd); + + } + else + { + if ((!impl_->predicate) || impl_->predicate()) + { + if (API::is_window(arg.window_handle)) + { + impl_->dragging = true; + using basic_window = ::nana::detail::basic_window; + auto real_wd = reinterpret_cast<::nana::detail::basic_window*>(arg.window_handle); + real_wd->other.dnd_state = dragdrop_status::in_progress; + + dragdrop_service::instance().dragdrop(arg.window_handle); + return; + } + } + + API::release_capture(impl_->window_handle); + } + + }); + + events.mouse_up.connect_unignorable([this]{ + if (impl_->cancel()) + { + auto drop_wd = API::find_window(API::cursor_position()); + auto i = impl_->targets.find(drop_wd); + if (impl_->targets.end() == i || !i->second) + return; + + i->second(); + } + }); + + events.key_press.connect_unignorable([this]{ + impl_->cancel(); + }); +#endif + } + + simple_dragdrop::~simple_dragdrop() + { + dragdrop_service::instance().remove(impl_->window_handle); + API::dev::window_draggable(impl_->window_handle, false); + delete impl_; + } + + void simple_dragdrop::condition(std::function predicate_fn) + { + impl_->predicate.swap(predicate_fn); + } + + void simple_dragdrop::make_drop(window target, std::function drop_fn) + { + dragdrop_service::instance().drop_assoc().add(impl_->window_handle, target); + impl_->targets[target].swap(drop_fn); + } +} \ No newline at end of file diff --git a/source/gui/programming_interface.cpp b/source/gui/programming_interface.cpp index 698aa543..1f3ac8c9 100644 --- a/source/gui/programming_interface.cpp +++ b/source/gui/programming_interface.cpp @@ -411,6 +411,26 @@ namespace API } } + + void window_draggable(window wd, bool enabled) + { + auto real_wd = reinterpret_cast(wd); + internal_scope_guard lock; + if (restrict::wd_manager().available(real_wd)) + real_wd->flags.draggable = enabled; + } + + bool window_draggable(window wd) + { + auto real_wd = reinterpret_cast(wd); + internal_scope_guard lock; + if (restrict::wd_manager().available(real_wd)) + return real_wd->flags.draggable; + + return false; + } + + }//end namespace dev widget* get_widget(window wd) @@ -1518,5 +1538,16 @@ namespace API { return ::nana::platform_abstraction::screen_dpi(x_requested); } + + dragdrop_status window_dragdrop_status(::nana::window wd) + { + auto real_wd = reinterpret_cast(wd); + internal_scope_guard lock; + + if (restrict::wd_manager().available(real_wd)) + return real_wd->other.dnd_state; + + return dragdrop_status::not_ready; + } }//end namespace API }//end namespace nana diff --git a/source/gui/widgets/listbox.cpp b/source/gui/widgets/listbox.cpp index e6a5c1c6..29a28ec1 100644 --- a/source/gui/widgets/listbox.cpp +++ b/source/gui/widgets/listbox.cpp @@ -1968,6 +1968,12 @@ namespace nana std::vector active_panes_; };//end class es_lister + enum class operation_states + { + none, + msup_deselect + }; + /// created and live by the trigger, holds data for listbox: the state of the struct does not effect on member funcions, therefore all data members are public. struct essence @@ -1981,7 +1987,9 @@ namespace nana bool auto_draw{true}; bool checkable{false}; bool if_image{false}; +#if 0 //deprecated bool deselect_deferred{ false }; //deselects items when mouse button is released. +#endif unsigned text_height; ::nana::listbox::export_options def_exp_options; @@ -1999,6 +2007,12 @@ namespace nana std::function ctg_icon_renderer; ///< Renderer for the category icon + struct operation_rep + { + operation_states state{operation_states::none}; + index_pair item; + }operation; + struct mouse_selection_part { bool started{ false }; @@ -2121,6 +2135,10 @@ namespace nana void update_mouse_selection(const point& screen_pos) { + //Don't update if it is not started + if (!mouse_selection.started) + return; + mouse_selection.screen_pos = screen_pos; auto logic_pos = coordinate_cast(screen_pos, true); @@ -4132,8 +4150,13 @@ namespace nana using item_state = essence::item_state; using parts = essence::parts; +#if 0 //deprecated //Cancel deferred deselection operation when mouse moves. essence_->deselect_deferred = false; +#else + if (operation_states::msup_deselect == essence_->operation.state) + essence_->operation.state = operation_states::none; +#endif bool need_refresh = false; @@ -4293,21 +4316,44 @@ namespace nana essence_->mouse_selection.reverse_selection = true; new_selected_status = !essence_->cs_status(abs_item_pos, true); } +#if 0 //deprecated + else if (nana::mouse::right_button == arg.button) + { + //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, abs_item_pos); + } else { - if (nana::mouse::right_button == arg.button) + //Unselects all selected items except current item if right button clicked. + lister.select_for_all(false, abs_item_pos); //cancel all selections + } +#else + else + { + auto selected = lister.pick_items(true); + if (selected.cend() != std::find(selected.cbegin(), selected.cend(), item_pos)) { - //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, abs_item_pos); + //If the current selected one has been selected before selecting, remains the selection states for all + //selected items. But these items will be unselected when the mouse is released. + + //Other items will be unselected if multiple items are selected. + if (selected.size() > 1) + { + essence_->operation.item = abs_item_pos; + + //Don't deselect the selections if the listbox is draggable. + //It should remain the selections for drag-and-drop + + if (!API::dev::window_draggable(arg.window_handle)) + essence_->operation.state = operation_states::msup_deselect; + } } else - { - //Unselects all selected items except current item if right button clicked. - lister.select_for_all(false, abs_item_pos); //cancel all selections - } + lister.select_for_all(false, abs_item_pos); } +#endif } else { @@ -4377,7 +4423,15 @@ namespace nana //Deselection of all items is deferred to the mouse up event when ctrl or shift is not pressed //Pressing ctrl or shift is to selects other items without deselecting current selections. +#if 0 //deprecated essence_->deselect_deferred = !(arg.ctrl || arg.shift); +#else + if (!(arg.ctrl || arg.shift)) + { + essence_->operation.state = operation_states::msup_deselect; + essence_->operation.item = index_pair{nana::npos, nana::npos}; + } +#endif } if(update) @@ -4419,11 +4473,19 @@ namespace nana need_refresh = true; } +#if 0 //deprecated if (essence_->deselect_deferred) { essence_->deselect_deferred = false; need_refresh |= (essence_->lister.select_for_all(false)); } +#endif + + if (operation_states::msup_deselect == essence_->operation.state) + { + essence_->operation.state = operation_states::none; + need_refresh |= essence_->lister.select_for_all(false, essence_->operation.item); + } if (need_refresh) { @@ -5790,6 +5852,18 @@ namespace nana return item_proxy(ess); } + listbox::index_pair listbox::hovered() const + { + internal_scope_guard lock; + using parts = drawerbase::listbox::essence::parts; + + auto & ptr_where = _m_ess().pointer_where; + if ((ptr_where.first == parts::list || ptr_where.first == parts::checker) && ptr_where.second != npos) + return _m_ess().lister.advance(_m_ess().first_display(), static_cast(ptr_where.second)); + + return index_pair{ npos, npos }; + } + bool listbox::sortable() const { internal_scope_guard lock; diff --git a/source/gui/widgets/skeletons/content_view.cpp b/source/gui/widgets/skeletons/content_view.cpp index 0596b4c6..cb7e291b 100644 --- a/source/gui/widgets/skeletons/content_view.cpp +++ b/source/gui/widgets/skeletons/content_view.cpp @@ -65,7 +65,7 @@ namespace nana { bool passive{ true }; //The passive mode determines whether to update if scrollbar changes. It updates the client window if passive is true. - bool drag_started{ false }; + bool drag_view_move{ false }; //indicates the status of the content-view if it moves origin by dragging point origin; std::shared_ptr cv_scroll; @@ -120,19 +120,22 @@ namespace nana { if (!arg.is_left_button()) return; - this->drag_started = this->view.view_area().is_hit(arg.pos); + this->drag_view_move = this->view.view_area().is_hit(arg.pos); } else if (event_code::mouse_move == arg.evt_code) { - if (this->drag_started && this->drive(arg.pos)) + if (this->drag_view_move && (dragdrop_status::not_ready == API::window_dragdrop_status(this->window_handle))) { - tmr.interval(16); - tmr.start(); + if (this->drive(arg.pos)) + { + tmr.interval(16); + tmr.start(); + } } } else if (event_code::mouse_up == arg.evt_code) { - this->drag_started = false; + this->drag_view_move = false; tmr.stop(); } }; From 5258b7ce8f81eb2c24de74fb76309224d105d924 Mon Sep 17 00:00:00 2001 From: qPCR4vir Date: Tue, 9 Oct 2018 17:54:39 +0200 Subject: [PATCH 060/149] update project from 8.1 to 10 ?? --- build/vc2017/nana.vcxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/vc2017/nana.vcxproj b/build/vc2017/nana.vcxproj index cc3c70dc..0c4323de 100644 --- a/build/vc2017/nana.vcxproj +++ b/build/vc2017/nana.vcxproj @@ -23,7 +23,7 @@ {42D0520F-EFA5-4831-84FE-2B9085301C5D} Win32Proj nana - 8.1 + 10.0.17134.0 From 1247e5c4e4c45ede36f846c7fdc854b2a8803a79 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Tue, 23 Oct 2018 03:31:34 +0800 Subject: [PATCH 061/149] add new msg dispatch method for filtering events --- source/detail/posix/msg_dispatcher.hpp | 34 ++++++++++++++++++++++---- source/detail/posix/msg_packet.hpp | 4 +-- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/source/detail/posix/msg_dispatcher.hpp b/source/detail/posix/msg_dispatcher.hpp index 9ba12ad1..f5b13bb9 100644 --- a/source/detail/posix/msg_dispatcher.hpp +++ b/source/detail/posix/msg_dispatcher.hpp @@ -143,7 +143,7 @@ namespace detail { //Make a cleanup msg packet to infor the dispatcher the window is closed. msg_packet_tag msg; - msg.kind = msg.kind_cleanup; + msg.kind = msg_packet_tag::pkt_family::cleanup; msg.u.packet_window = wd; thr->msg_queue.push_back(msg); } @@ -171,6 +171,30 @@ namespace detail } } } + + template + void dispatch(MsgFilter msg_filter_fn) + { + auto tid = nana::system::this_thread_id(); + msg_packet_tag msg; + int qstate; + + //Test whether the thread is registered for window, and retrieve the queue state for event + while((qstate = _m_read_queue(tid, msg, 0))) + { + //the queue is empty + if(-1 == qstate) + { + if(false == _m_wait_for_queue(tid)) + proc_.timer_proc(tid); + } + else + { + if(msg_filter_fn(msg)) + return; + } + } + } private: void _m_msg_driver() { @@ -220,7 +244,7 @@ namespace detail switch(proc_.filter_proc(event, msg_pack)) { case 0: - msg_pack.kind = msg_pack.kind_xevent; + msg_pack.kind = msg_packet_tag::pkt_family::xevent; msg_pack.u.xevent = event; _m_msg_dispatch(msg_pack); break; @@ -246,9 +270,9 @@ namespace detail { switch(pack.kind) { - case msg_packet_tag::kind_xevent: + case msg_packet_tag::pkt_family::xevent: return _m_event_window(pack.u.xevent); - case msg_packet_tag::kind_mouse_drop: + case msg_packet_tag::pkt_family::mouse_drop: return pack.u.mouse_drop.window; default: break; @@ -294,7 +318,7 @@ namespace detail //Check whether the event dispatcher is used for the modal window //and when the modal window is closing, the event dispatcher would //stop event pumping. - if((modal == msg.u.packet_window) && (msg.kind == msg.kind_cleanup)) + if((modal == msg.u.packet_window) && (msg.kind == msg_packet_tag::pkt_family::cleanup)) return 0; return 1; diff --git a/source/detail/posix/msg_packet.hpp b/source/detail/posix/msg_packet.hpp index ea24091f..fbc0848f 100644 --- a/source/detail/posix/msg_packet.hpp +++ b/source/detail/posix/msg_packet.hpp @@ -10,8 +10,8 @@ namespace detail { struct msg_packet_tag { - enum kind_t{kind_xevent, kind_mouse_drop, kind_cleanup}; - kind_t kind; + enum class pkt_family{xevent, mouse_drop, cleanup}; + pkt_family kind; union { XEvent xevent; From f32f22f429f6664759f542e3a2dbeba9874029aa Mon Sep 17 00:00:00 2001 From: Jinhao Date: Tue, 23 Oct 2018 03:34:12 +0800 Subject: [PATCH 062/149] implement dragdrop for X11 --- source/detail/platform_spec_posix.cpp | 42 ++++- source/detail/posix/platform_spec.hpp | 19 +++ source/gui/detail/bedrock_posix.cpp | 6 +- source/gui/dragdrop.cpp | 229 +++++++++++++++++++++++++- 4 files changed, 285 insertions(+), 11 deletions(-) diff --git a/source/detail/platform_spec_posix.cpp b/source/detail/platform_spec_posix.cpp index 62cb293a..46b80f2a 100644 --- a/source/detail/platform_spec_posix.cpp +++ b/source/detail/platform_spec_posix.cpp @@ -1041,6 +1041,12 @@ namespace detail msg_dispatcher_->dispatch(reinterpret_cast(modal)); } + void platform_spec::msg_dispatch(std::function msg_filter_fn) + { + msg_dispatcher_->dispatch(msg_filter_fn); + + } + void* platform_spec::request_selection(native_window_type requestor, Atom type, size_t& size) { if(requestor) @@ -1104,6 +1110,40 @@ namespace detail return graph; } + + bool platform_spec::register_dragdrop(native_window_type wd, dragdrop_interface* ddrop) + { + platform_scope_guard lock; + if(0 != xdnd_.dragdrop.count(wd)) + return false; + + int dndver = 4; + ::XChangeProperty(display_, reinterpret_cast(wd), atombase_.xdnd_aware, XA_ATOM, sizeof(int) * 8, + PropModeReplace, reinterpret_cast(&dndver), 1); + + auto & ref_drop = xdnd_.dragdrop[wd]; + ref_drop.dragdrop = ddrop; + ref_drop.ref_count = 1; + return true; + } + + dragdrop_interface* platform_spec::remove_dragdrop(native_window_type wd) + { + platform_scope_guard lock; + auto i = xdnd_.dragdrop.find(wd); + if(i == xdnd_.dragdrop.end()) + return nullptr; + + auto ddrop = i->second; + if(ddrop.ref_count <= 1) + { + xdnd_.dragdrop.erase(i); + return ddrop.dragdrop; + } + --ddrop.ref_count; + return nullptr; + } + //_m_msg_filter //@return: _m_msg_filter returns three states // 0 = msg_dispatcher dispatches the XEvent @@ -1163,7 +1203,7 @@ namespace detail else if(evt.xselection.property == self.atombase_.xdnd_selection) { bool accepted = false; - msg.kind = msg.kind_mouse_drop; + msg.kind = msg_packet_tag::pkt_family::mouse_drop; msg.u.mouse_drop.window = 0; if(bytes_left > 0 && type == self.xdnd_.good_type) { diff --git a/source/detail/posix/platform_spec.hpp b/source/detail/posix/platform_spec.hpp index 3191ddb1..af472a92 100644 --- a/source/detail/posix/platform_spec.hpp +++ b/source/detail/posix/platform_spec.hpp @@ -36,6 +36,7 @@ #include #include +#include #include "msg_packet.hpp" #include "../platform_abstraction_types.hpp" @@ -176,6 +177,12 @@ namespace detail ~platform_scope_guard(); }; + class dragdrop_interface + { + public: + virtual ~dragdrop_interface() = default; + }; + class platform_spec { typedef platform_spec self_type; @@ -246,6 +253,7 @@ namespace detail void msg_insert(native_window_type); void msg_set(timer_proc_type, event_proc_type); void msg_dispatch(native_window_type modal); + void msg_dispatch(std::function); //X Selections void* request_selection(native_window_type requester, Atom type, size_t & bufsize); @@ -255,6 +263,9 @@ namespace detail //@biref: The image object should be kept for a long time till the window is closed, // the image object is release in remove() method. const nana::paint::graphics& keep_window_icon(native_window_type, const nana::paint::image&); + + bool register_dragdrop(native_window_type, dragdrop_interface*); + dragdrop_interface* remove_dragdrop(native_window_type); private: static int _m_msg_filter(XEvent&, msg_packet_tag&); void _m_caret_routine(); @@ -311,6 +322,14 @@ namespace detail int timestamp; Window wd_src; nana::point pos; + + struct refcount_dragdrop + { + dragdrop_interface* dragdrop{nullptr}; + std::size_t ref_count{0}; + }; + + std::map dragdrop; }xdnd_; msg_dispatcher * msg_dispatcher_; diff --git a/source/gui/detail/bedrock_posix.cpp b/source/gui/detail/bedrock_posix.cpp index 0504cf95..cbee18d2 100644 --- a/source/gui/detail/bedrock_posix.cpp +++ b/source/gui/detail/bedrock_posix.cpp @@ -418,10 +418,10 @@ namespace detail { switch(msg.kind) { - case nana::detail::msg_packet_tag::kind_xevent: + case nana::detail::msg_packet_tag::pkt_family::xevent: window_proc_for_xevent(display, msg.u.xevent); break; - case nana::detail::msg_packet_tag::kind_mouse_drop: + case nana::detail::msg_packet_tag::pkt_family::mouse_drop: window_proc_for_packet(display, msg); break; default: break; @@ -441,7 +441,7 @@ namespace detail switch(msg.kind) { - case nana::detail::msg_packet_tag::kind_mouse_drop: + case nana::detail::msg_packet_tag::pkt_family::mouse_drop: msgwd = brock.wd_manager().find_window(native_window, {msg.u.mouse_drop.x, msg.u.mouse_drop.y}); if(msgwd) { diff --git a/source/gui/dragdrop.cpp b/source/gui/dragdrop.cpp index a267890b..a4323dc2 100644 --- a/source/gui/dragdrop.cpp +++ b/source/gui/dragdrop.cpp @@ -10,6 +10,7 @@ * @file: nana/gui/dragdrop.cpp * @author: Jinhao(cnjinhao@hotmail.com) */ + #include #include @@ -24,6 +25,11 @@ # include # include # include +#elif defined(NANA_X11) +# include "../detail/posix/platform_spec.hpp" +# include +# include +# include #endif namespace nana @@ -367,6 +373,60 @@ namespace nana private: std::vector mediums_; }; +#elif defined(NANA_X11) + class x11_dragdrop: public detail::dragdrop_interface + { + + }; + + class shared_icons + { + public: + shared_icons() + { + path_ = "/usr/share/icons/"; + ifs_.open(path_ + "default/index.theme"); + } + + std::string cursor(const std::string& name) + { + auto theme = _m_read("Icon Theme", "Inherits"); + + return path_ + theme + "/cursors/" + name; + } + private: + std::string _m_read(const std::string& category, const std::string& key) + { + ifs_.seekg(0, std::ios::beg); + + bool found_cat = false; + while(ifs_.good()) + { + std::string text; + std::getline(ifs_, text); + + if(0 == text.find('[')) + { + if(found_cat) + break; + + if(text.find(category + "]") != text.npos) + { + found_cat = true; + } + } + else if(found_cat && (text.find(key + "=") == 0)) + { + return text.substr(key.size() + 1); + } + } + + return {}; + } + private: + std::string path_; + std::ifstream ifs_; + }; #endif class dragdrop_service @@ -385,6 +445,7 @@ namespace nana if (nullptr == native_wd) return; +#ifdef NANA_WINDOWS if(table_.empty()) ::OleInitialize(nullptr); @@ -403,22 +464,33 @@ namespace nana drop_target = i->second; drop_target->AddRef(); } +#elif defined(NANA_X11) + auto ddrop = new x11_dragdrop; + if(!_m_spec().register_dragdrop(native_wd, ddrop)) + delete ddrop; +#endif } void remove(window wd) { +#ifdef NANA_WINDOWS auto i = table_.find(API::root(wd)); if (i != table_.end()) { if (0 == i->second->Release()) table_.erase(i); } - +#elif defined(NANA_X11) + auto ddrop = _m_spec().remove_dragdrop(API::root(wd)); + delete ddrop; +#endif drop_assoc_.erase(wd); + } bool dragdrop(window drag_wd) { +#ifdef NANA_WINDOWS auto i = table_.find(API::root(drag_wd)); if (table_.end() == i) return false; @@ -438,19 +510,158 @@ namespace nana auto status = ::DoDragDrop(drop_dat, drop_src, DROPEFFECT_COPY, &eff); i->second->set_source(nullptr); + return true; +#elif defined(NANA_X11) + auto const native_wd = reinterpret_cast(API::root(drag_wd)); + + { + detail::platform_scope_guard lock; + ::XSetSelectionOwner(_m_spec().open_display(), _m_spec().atombase().xdnd_selection, native_wd, CurrentTime); + } + + + hovered_.window_handle = nullptr; + hovered_.native_wd = 0; + window target_wd = 0; + auto& atombase = _m_spec().atombase(); + //while(true) + { + + _m_spec().msg_dispatch([this, drag_wd, native_wd, &target_wd, &atombase](const detail::msg_packet_tag& msg_pkt) mutable{ + if(detail::msg_packet_tag::pkt_family::xevent == msg_pkt.kind) + { + auto const disp = _m_spec().open_display(); + if (MotionNotify == msg_pkt.u.xevent.type) + { + auto pos = API::cursor_position(); + auto native_cur_wd = reinterpret_cast(detail::native_interface::find_window(pos.x, pos.y)); + + if(hovered_.native_wd != native_cur_wd) + { + if(hovered_.native_wd) + { + _m_free_cursor(); + ::XUndefineCursor(disp, hovered_.native_wd); + } + + _m_client_msg(native_cur_wd, native_wd, atombase.xdnd_enter, atombase.text_uri_list, XA_STRING); + hovered_.native_wd = native_cur_wd; + } + + auto cur_wd = API::find_window(API::cursor_position()); + + if(hovered_.window_handle != cur_wd) + { + _m_free_cursor(); + + hovered_.window_handle = cur_wd; + + if((drag_wd == cur_wd) || drop_assoc_.has(drag_wd, cur_wd)) + hovered_.cursor = ::XcursorFilenameLoadCursor(disp, icons_.cursor("dnd-move").c_str()); + else + hovered_.cursor = ::XcursorFilenameLoadCursor(disp, icons_.cursor("dnd-none").c_str()); + ::XDefineCursor(disp, native_cur_wd, hovered_.cursor); + } + } + else if(msg_pkt.u.xevent.type == ButtonRelease) + { + target_wd = API::find_window(API::cursor_position()); + ::XUndefineCursor(disp, hovered_.native_wd); + _m_free_cursor(); + return true; + } + + } + return false; + }); + } + + return (nullptr != target_wd); +#endif + return false; } drop_association& drop_assoc() { return drop_assoc_; } +#ifdef NANA_X11 + private: + static nana::detail::platform_spec & _m_spec() + { + return nana::detail::platform_spec::instance(); + } + + //dndversion<<24, fl_XdndURIList, XA_STRING, 0 + static void _m_client_msg(Window wd_target, Window wd_src, Atom xdnd_atom, Atom data, Atom data_type) + { + auto const display = _m_spec().open_display(); + XEvent evt; + ::memset(&evt, 0, sizeof evt); + evt.xany.type = ClientMessage; + evt.xany.display = display; + evt.xclient.window = wd_target; + evt.xclient.message_type = xdnd_atom; + evt.xclient.format = 32; + + //Target window + evt.xclient.data.l[0] = wd_src; + //Accept set + evt.xclient.data.l[1] = 1; + evt.xclient.data.l[2] = data; + evt.xclient.data.l[3] = data_type; + evt.xclient.data.l[4] = 0; + + ::XSendEvent(display, wd_target, True, NoEventMask, &evt); + + } + + static int _m_xdnd_aware(Window wd) + { + Atom actual; int format; unsigned long count, remaining; + unsigned char *data = 0; + XGetWindowProperty(_m_spec().open_display(), wd, _m_spec().atombase().xdnd_aware, + 0, 4, False, XA_ATOM, &actual, &format, &count, &remaining, &data); + + int version = 0; + if ((actual == XA_ATOM) && (format==32) && count && data) + version = int(*(Atom*)data); + + if (data) + ::XFree(data); + return version; + } + + void _m_free_cursor() + { + if(hovered_.cursor) + { + ::XFreeCursor(_m_spec().open_display(), hovered_.cursor); + hovered_.cursor = 0; + } + + } +#endif private: - std::map table_; drop_association drop_assoc_; +#ifdef NANA_WINDOWS + std::map table_; +#elif defined (NANA_X11) + shared_icons icons_; + struct hovered_status + { + Window native_wd{0}; + window window_handle{nullptr}; + + unsigned shape{0}; + Cursor cursor{0}; + }hovered_; +#endif }; + struct simple_dragdrop::implementation { window window_handle; @@ -459,7 +670,7 @@ namespace nana bool dragging{ false }; -#if 0 +#ifdef NANA_X11 bool cancel() { if (!dragging) @@ -499,11 +710,15 @@ namespace nana auto & events = API::events<>(drag_wd); -#ifdef NANA_WINDOWS +#if 1 //#ifdef NANA_WINDOWS events.mouse_down.connect_unignorable([this](const arg_mouse& arg){ if (arg.is_left_button() && API::is_window(impl_->window_handle)) { impl_->dragging = ((!impl_->predicate) || impl_->predicate()); + + using basic_window = ::nana::detail::basic_window; + auto real_wd = reinterpret_cast<::nana::detail::basic_window*>(impl_->window_handle); + real_wd->other.dnd_state = dragdrop_status::ready; } }); @@ -528,11 +743,11 @@ namespace nana i->second(); } }); -#else +#elif 1 events.mouse_down.connect_unignorable([drag_wd](const arg_mouse& arg){ if (arg.is_left_button() && API::is_window(drag_wd)) { - API::set_capture(drag_wd, true); + //API::set_capture(drag_wd, true); using basic_window = ::nana::detail::basic_window; auto real_wd = reinterpret_cast<::nana::detail::basic_window*>(drag_wd); @@ -566,7 +781,7 @@ namespace nana } } - API::release_capture(impl_->window_handle); + //API::release_capture(impl_->window_handle); } }); From ff9b90a76650ed79f47cd341c4bf8725bb97157a Mon Sep 17 00:00:00 2001 From: Jinhao Date: Tue, 23 Oct 2018 03:40:54 +0800 Subject: [PATCH 063/149] add libXcursor-dev --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index d98c0dcf..47cc31ef 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,6 +20,7 @@ matrix: - alsa-oss - libx11-dev - libxft-dev + - libxcursor-dev sources: - ubuntu-toolchain-r-test @@ -36,6 +37,7 @@ matrix: - alsa-oss - libx11-dev - libxft-dev + - libxcursor-dev sources: - ubuntu-toolchain-r-test - llvm-toolchain-precise From c0862ba666d662e71a13a761e77877810373fe19 Mon Sep 17 00:00:00 2001 From: qPCR4vir Date: Thu, 25 Oct 2018 00:56:09 +0200 Subject: [PATCH 064/149] FIX folderbox with canonical make_prefered and add title --- include/nana/gui/filebox.hpp | 15 +++++--- source/gui/filebox.cpp | 75 ++++++++++++++++++++++-------------- 2 files changed, 56 insertions(+), 34 deletions(-) diff --git a/include/nana/gui/filebox.hpp b/include/nana/gui/filebox.hpp index f6729318..812770ee 100644 --- a/include/nana/gui/filebox.hpp +++ b/include/nana/gui/filebox.hpp @@ -9,7 +9,7 @@ * * @file nana/gui/filebox.hpp * @author Jinhao - * @brief a dialog to chose file(s), implemented "native" in windows but using nana for X11 + * @brief dialogs to chose file(s) or a directory, implemented "native" in windows but using nana for X11 */ #ifndef NANA_GUI_FILEBOX_HPP @@ -31,7 +31,7 @@ namespace nana public: using filters = std::vector>; - filebox(bool is_open_mode); + explicit filebox(bool is_open_mode); filebox(window owner, bool is_open_mode); filebox(const filebox&); ~filebox(); @@ -56,8 +56,8 @@ namespace nana /// \brief Add a filetype filter. /// To specify multiple filter in a single description, use a semicolon to separate the patterns(for example,"*.TXT;*.DOC;*.BAK"). - filebox& add_filter(const ::std::string& description, ///< for example. "Text File" - const ::std::string& filetype ///< filter pattern(for example, "*.TXT") + filebox& add_filter(const ::std::string& description, ///< for example: "Text File" + const ::std::string& filetype ///< filter pattern(for example: "*.TXT") ); filebox& add_filter(const filters &ftres) @@ -94,7 +94,7 @@ namespace nana public: using path_type = std::experimental::filesystem::path; - folderbox(window owner = nullptr, const path_type& init_path = {}); + explicit folderbox(window owner = nullptr, const path_type& init_path = {}, std::string title={}); ~folderbox(); std::optional show() const; @@ -103,6 +103,11 @@ namespace nana { return show(); } + + /// Set a new title for the dialog + /// @param string a text for title + /// @return the old title. + ::std::string title( ::std::string new_title); private: implement* impl_; }; diff --git a/source/gui/filebox.cpp b/source/gui/filebox.cpp index fcaba515..16c69d0f 100644 --- a/source/gui/filebox.cpp +++ b/source/gui/filebox.cpp @@ -10,6 +10,8 @@ * @file: nana/gui/filebox.cpp */ +#include + #include #include #include @@ -1114,29 +1116,29 @@ namespace nana for(auto & f : impl_->filters) { filter_holder += to_wstring(f.des); - filter_holder += static_cast('\0'); - std::wstring fs = to_wstring(f.type); + filter_holder += static_cast('\0'); // separator + std::wstring ff = to_wstring(f.type); std::size_t pos = 0; - while(true) + while(true) // eliminate spaces { - pos = fs.find(L" ", pos); - if(pos == fs.npos) + pos = ff.find(L" ", pos); + if(pos == ff.npos) break; - fs.erase(pos); + ff.erase(pos); } - filter_holder += fs; - filter_holder += static_cast('\0'); + filter_holder += ff; + filter_holder += static_cast('\0'); // separator - //Get the default file extentsion + //Get the default file extension if (default_extension.empty()) { - pos = fs.find_last_of('.'); - if (pos != fs.npos) + pos = ff.find_last_of('.'); + if (pos != ff.npos) { - fs = fs.substr(pos + 1); - if (fs != L"*") + ff = ff.substr(pos + 1); + if (ff != L"*") { - default_extension = fs; + default_extension = ff; ofn.lpstrDefExt = default_extension.data(); } } @@ -1207,25 +1209,33 @@ namespace nana }//end class filebox - //class directory_picker + //class directory picker struct folderbox::implement { window owner; path_type init_path; + std::string title; + }; - folderbox::folderbox(window owner, const path_type& init_path) - : impl_(new implement) - { - impl_->owner = owner; - impl_->init_path = init_path; - } + folderbox::folderbox(window owner, const path_type& init_path, std::string title) + : impl_(new implement{ owner, fs::canonical(init_path).make_preferred(), title }) + {} + folderbox::~folderbox() { delete impl_; } + + std::string folderbox::title(std::string s) + { + impl_->title.swap(s); + return s; + } + + #ifdef NANA_MINGW static int CALLBACK browse_folder_callback(HWND hwnd, UINT msg, LPARAM lparam, LPARAM data) { @@ -1235,7 +1245,9 @@ namespace nana { case BFFM_INITIALIZED: if (data) + { SendMessage(hwnd, BFFM_SETSELECTION, TRUE, data); + } break; } @@ -1278,17 +1290,22 @@ namespace nana fd->Release(); } #else - BROWSEINFO brw = { 0 }; wchar_t display_text[MAX_PATH]; - brw.hwndOwner = reinterpret_cast(API::root(impl_->owner)); - brw.pszDisplayName = display_text; - brw.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE; - brw.lpfn = browse_folder_callback; - + auto title = to_wstring( impl_->title ) ; std::wstring init_path = impl_->init_path.wstring(); - brw.lParam = reinterpret_cast(init_path.c_str()); - auto pidl = ::SHBrowseForFolder(&brw); + // https://docs.microsoft.com/en-us/windows/desktop/api/shlobj_core/ns-shlobj_core-_browseinfoa + BROWSEINFO brw = { 0 }; + brw.hwndOwner = reinterpret_cast(API::root(impl_->owner)); + //brw.pidlRoot; // specifies the location of the root folder from which to start browsing. + brw.pszDisplayName = display_text; // buffer to receive the display name of the folder selected by the user. + brw.lpszTitle = title.data(); + brw.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE; // | BIF_EDITBOX; + brw.lpfn = browse_folder_callback; + brw.lParam = reinterpret_cast(init_path.c_str()); + //brw.iImage; // + + auto pidl = ::SHBrowseForFolder(&brw); if (pidl) { wchar_t folder_path[MAX_PATH]; From bde0d162432a6e76805670300eeb92073e022635 Mon Sep 17 00:00:00 2001 From: besh81 Date: Fri, 26 Oct 2018 17:12:22 +0200 Subject: [PATCH 065/149] Added tabbar adding event Fired when the add button of the tabbar is pressed. Changing the value of arg.add to false the action can be vetoed. The value arg.where indicates the position of the new item. --- include/nana/gui/widgets/tabbar.hpp | 22 ++++++++++++++++++++++ source/gui/widgets/tabbar.cpp | 12 ++++++++++++ 2 files changed, 34 insertions(+) diff --git a/include/nana/gui/widgets/tabbar.hpp b/include/nana/gui/widgets/tabbar.hpp index 17a57411..0266ba20 100644 --- a/include/nana/gui/widgets/tabbar.hpp +++ b/include/nana/gui/widgets/tabbar.hpp @@ -35,6 +35,19 @@ namespace nana {} }; + template + struct arg_tabbar_adding + : public event_arg + { + arg_tabbar_adding(tabbar& wdg, std::size_t pos) + : widget(wdg), where(pos) + {} + + tabbar & widget; + mutable bool add = true; ///< determines whether to add the item + std::size_t where; ///< position where to add the item + }; + template struct arg_tabbar_removed : public arg_tabbar { @@ -56,6 +69,7 @@ namespace nana { using value_type = T; + basic_event> adding; basic_event> added; basic_event> activated; basic_event> removed; @@ -65,6 +79,7 @@ namespace nana { public: virtual ~event_agent_interface() = default; + virtual bool adding(std::size_t) = 0; virtual void added(std::size_t) = 0; virtual void activated(std::size_t) = 0; virtual bool removed(std::size_t, bool & close_attached) = 0; @@ -107,6 +122,13 @@ namespace nana : tabbar_(tb), drawer_trigger_(dtr) {} + bool adding(std::size_t pos) override + { + ::nana::arg_tabbar_adding arg(tabbar_, pos); + tabbar_.events().adding.emit(arg, tabbar_); + return arg.add; + } + void added(std::size_t pos) override { if(pos != npos) diff --git a/source/gui/widgets/tabbar.cpp b/source/gui/widgets/tabbar.cpp index 8970089b..d5a05907 100644 --- a/source/gui/widgets/tabbar.cpp +++ b/source/gui/widgets/tabbar.cpp @@ -751,15 +751,27 @@ namespace nana if((pos == npos) || (pos >= list_.size())) { pos = list_.size(); + + if(evt_agent_) + if(!evt_agent_->adding(pos)) + return false; + this->list_.emplace_back(); } else + { + if(evt_agent_) + if(!evt_agent_->adding(pos)) + return false; + list_.emplace(iterator_at(pos)); + } basis_.active = pos; if(evt_agent_) { evt_agent_->added(pos); + erase(pos); evt_agent_->activated(pos); } return true; From 711dff56de40a4f0f6054a6d1c567f8d525a10ea Mon Sep 17 00:00:00 2001 From: Jinhao Date: Sat, 27 Oct 2018 17:46:22 +0800 Subject: [PATCH 066/149] add simple_dragdrop feature --- source/detail/platform_spec_posix.cpp | 18 +- source/detail/posix/platform_spec.hpp | 19 +- source/gui/dragdrop.cpp | 260 +++++++++++++++++--------- 3 files changed, 182 insertions(+), 115 deletions(-) diff --git a/source/detail/platform_spec_posix.cpp b/source/detail/platform_spec_posix.cpp index 46b80f2a..f76b7266 100644 --- a/source/detail/platform_spec_posix.cpp +++ b/source/detail/platform_spec_posix.cpp @@ -1111,7 +1111,7 @@ namespace detail } - bool platform_spec::register_dragdrop(native_window_type wd, dragdrop_interface* ddrop) + bool platform_spec::register_dragdrop(native_window_type wd, x11_dragdrop_interface* ddrop) { platform_scope_guard lock; if(0 != xdnd_.dragdrop.count(wd)) @@ -1121,13 +1121,11 @@ namespace detail ::XChangeProperty(display_, reinterpret_cast(wd), atombase_.xdnd_aware, XA_ATOM, sizeof(int) * 8, PropModeReplace, reinterpret_cast(&dndver), 1); - auto & ref_drop = xdnd_.dragdrop[wd]; - ref_drop.dragdrop = ddrop; - ref_drop.ref_count = 1; + xdnd_.dragdrop[wd] = ddrop; return true; } - dragdrop_interface* platform_spec::remove_dragdrop(native_window_type wd) + x11_dragdrop_interface* platform_spec::remove_dragdrop(native_window_type wd) { platform_scope_guard lock; auto i = xdnd_.dragdrop.find(wd); @@ -1135,13 +1133,9 @@ namespace detail return nullptr; auto ddrop = i->second; - if(ddrop.ref_count <= 1) - { - xdnd_.dragdrop.erase(i); - return ddrop.dragdrop; - } - --ddrop.ref_count; - return nullptr; + xdnd_.dragdrop.erase(i); + + return ddrop; } //_m_msg_filter diff --git a/source/detail/posix/platform_spec.hpp b/source/detail/posix/platform_spec.hpp index af472a92..f639beba 100644 --- a/source/detail/posix/platform_spec.hpp +++ b/source/detail/posix/platform_spec.hpp @@ -177,10 +177,13 @@ namespace detail ~platform_scope_guard(); }; - class dragdrop_interface + class x11_dragdrop_interface { public: - virtual ~dragdrop_interface() = default; + virtual ~x11_dragdrop_interface() = default; + + virtual void add_ref() = 0; + virtual std::size_t release() = 0; }; class platform_spec @@ -264,8 +267,8 @@ namespace detail // the image object is release in remove() method. const nana::paint::graphics& keep_window_icon(native_window_type, const nana::paint::image&); - bool register_dragdrop(native_window_type, dragdrop_interface*); - dragdrop_interface* remove_dragdrop(native_window_type); + bool register_dragdrop(native_window_type, x11_dragdrop_interface*); + x11_dragdrop_interface* remove_dragdrop(native_window_type); private: static int _m_msg_filter(XEvent&, msg_packet_tag&); void _m_caret_routine(); @@ -323,13 +326,7 @@ namespace detail Window wd_src; nana::point pos; - struct refcount_dragdrop - { - dragdrop_interface* dragdrop{nullptr}; - std::size_t ref_count{0}; - }; - - std::map dragdrop; + std::map dragdrop; }xdnd_; msg_dispatcher * msg_dispatcher_; diff --git a/source/gui/dragdrop.cpp b/source/gui/dragdrop.cpp index a4323dc2..4751a62a 100644 --- a/source/gui/dragdrop.cpp +++ b/source/gui/dragdrop.cpp @@ -32,38 +32,65 @@ # include #endif + namespace nana { - /// drop_association - /** - * This class is used for querying whether tow windows have a connection of drag and drop - */ - class drop_association + class dragdrop_session { public: - void add(window source, window target) + virtual ~dragdrop_session() = default; + + void insert(window source, window target) { - assoc_[source].insert(target); + table_[source].insert(target); } - void erase(window wd) + void erase(window source, window target) { - assoc_.erase(wd); + auto i = table_.find(source); + if (table_.end() == i) + return; - for (auto & assoc : assoc_) - assoc.second.erase(wd); + i->second.erase(target); + + if ((nullptr == target) || i->second.empty()) + table_.erase(i); } bool has(window source, window target) const { - auto i = assoc_.find(source); - if (i != assoc_.end()) + auto i = table_.find(source); + if (i != table_.end()) return (0 != i->second.count(target)); - + return false; } + + bool has_source(window source) const + { + return (table_.count(source) != 0); + } + + bool empty() const + { + return table_.empty(); + } + + void set_current_source(window source) + { + if (table_.count(source)) + current_source_ = source; + else + current_source_ = nullptr; + } + + window current_source() const + { + return current_source_; + } private: - std::map> assoc_; + std::map> table_; + window current_source_{ nullptr }; }; #ifdef NANA_WINDOWS @@ -98,18 +125,8 @@ namespace nana LONG ref_count_{ 1 }; }; - - class win32com_drop_target : public IDropTarget + class win32com_drop_target : public IDropTarget, public dragdrop_session { - public: - win32com_drop_target(const drop_association& drop_assoc) : - drop_assoc_(drop_assoc) - {} - - void set_source(window wd) - { - source_window_ = wd; - } public: //Implements IUnknown STDMETHODIMP QueryInterface(REFIID riid, void **ppv) @@ -146,11 +163,11 @@ namespace nana STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) { auto hovered_wd = API::find_window(point(pt.x, pt.y)); - if ((hovered_wd && (hovered_wd == source_window_)) || drop_assoc_.has(source_window_, hovered_wd)) + + if ((hovered_wd && (hovered_wd == this->current_source())) || this->has(this->current_source(), hovered_wd)) *pdwEffect &= DROPEFFECT_COPY; else *pdwEffect = DROPEFFECT_NONE; - return S_OK; } @@ -165,9 +182,6 @@ namespace nana } private: LONG ref_count_{ 1 }; - - window source_window_{ nullptr }; - const drop_association& drop_assoc_; }; class drop_source : public win32com_iunknown @@ -374,9 +388,25 @@ namespace nana std::vector mediums_; }; #elif defined(NANA_X11) - class x11_dragdrop: public detail::dragdrop_interface + class x11_dragdrop: public detail::x11_dragdrop_interface, public dragdrop_session { + public: + //Implement x11_dragdrop_interface + void add_ref() override + { + ++ref_count_; + } + + std::size_t release() override + { + std::size_t val = --ref_count_; + if(0 == val) + delete this; + return val; + } + private: + std::atomic ref_count_{ 1 }; }; class shared_icons @@ -433,68 +463,89 @@ namespace nana { dragdrop_service() = default; public: +#ifdef NANA_WINDOWS + using dragdrop_target = win32com_drop_target; +#else + using dragdrop_target = x11_dragdrop; +#endif + static dragdrop_service& instance() { static dragdrop_service serv; return serv; } - void create_dragdrop(window wd) + dragdrop_session* create_dragdrop(window wd) { auto native_wd = API::root(wd); if (nullptr == native_wd) - return; + return nullptr; -#ifdef NANA_WINDOWS - if(table_.empty()) - ::OleInitialize(nullptr); - - win32com_drop_target* drop_target = nullptr; + dragdrop_target * ddrop = nullptr; auto i = table_.find(native_wd); - if (i == table_.end()) + if(table_.end() == i) { - drop_target = new win32com_drop_target{drop_assoc_}; - ::RegisterDragDrop(reinterpret_cast(native_wd), drop_target); + ddrop = new dragdrop_target; +#ifdef NANA_WINDOWS + if (table_.empty()) + ::OleInitialize(nullptr); - table_[native_wd] = drop_target; + ::RegisterDragDrop(reinterpret_cast(native_wd), ddrop); +#else + if(!_m_spec().register_dragdrop(native_wd, ddrop)) + { + delete ddrop; + return nullptr; + } +#endif + table_[native_wd] = ddrop; } else { - drop_target = i->second; - drop_target->AddRef(); - } -#elif defined(NANA_X11) - auto ddrop = new x11_dragdrop; - if(!_m_spec().register_dragdrop(native_wd, ddrop)) - delete ddrop; + ddrop = dynamic_cast(i->second); + +#ifdef NANA_WINDOWS + ddrop->AddRef(); +#else + ddrop->add_ref(); #endif + } + + return ddrop; } - void remove(window wd) + void remove(window source) { -#ifdef NANA_WINDOWS - auto i = table_.find(API::root(wd)); - if (i != table_.end()) - { - if (0 == i->second->Release()) - table_.erase(i); - } -#elif defined(NANA_X11) - auto ddrop = _m_spec().remove_dragdrop(API::root(wd)); - delete ddrop; -#endif - drop_assoc_.erase(wd); + auto native_src = API::root(source); + auto i = table_.find(API::root(source)); + if (i == table_.end()) + return; + + i->second->erase(source, nullptr); + + if (i->second->empty()) + { + auto ddrop = dynamic_cast(i->second); + table_.erase(i); +#ifdef NANA_WINDOWS + ::RevokeDragDrop(reinterpret_cast(native_src)); + ddrop->Release(); +#elif defined(NANA_X11) + _m_spec().remove_dragdrop(native_src); + ddrop->release(); +#endif + } } bool dragdrop(window drag_wd) { -#ifdef NANA_WINDOWS auto i = table_.find(API::root(drag_wd)); if (table_.end() == i) return false; +#ifdef NANA_WINDOWS auto drop_src = new drop_source{ drag_wd }; auto drop_dat = new (std::nothrow) drop_data; if (!drop_dat) @@ -503,21 +554,25 @@ namespace nana return false; } - i->second->set_source(drag_wd); - + i->second->set_current_source(drag_wd); DWORD eff; auto status = ::DoDragDrop(drop_dat, drop_src, DROPEFFECT_COPY, &eff); - i->second->set_source(nullptr); + i->second->set_current_source(nullptr); + + delete drop_src; + delete drop_dat; return true; #elif defined(NANA_X11) - auto const native_wd = reinterpret_cast(API::root(drag_wd)); + auto ddrop = dynamic_cast(i->second); + + auto const native_source = reinterpret_cast(API::root(drag_wd)); { detail::platform_scope_guard lock; - ::XSetSelectionOwner(_m_spec().open_display(), _m_spec().atombase().xdnd_selection, native_wd, CurrentTime); + ::XSetSelectionOwner(_m_spec().open_display(), _m_spec().atombase().xdnd_selection, native_source, CurrentTime); } @@ -528,7 +583,7 @@ namespace nana //while(true) { - _m_spec().msg_dispatch([this, drag_wd, native_wd, &target_wd, &atombase](const detail::msg_packet_tag& msg_pkt) mutable{ + _m_spec().msg_dispatch([this, ddrop, drag_wd, native_source, &target_wd, &atombase](const detail::msg_packet_tag& msg_pkt) mutable{ if(detail::msg_packet_tag::pkt_family::xevent == msg_pkt.kind) { auto const disp = _m_spec().open_display(); @@ -545,23 +600,22 @@ namespace nana ::XUndefineCursor(disp, hovered_.native_wd); } - _m_client_msg(native_cur_wd, native_wd, atombase.xdnd_enter, atombase.text_uri_list, XA_STRING); + _m_client_msg(native_cur_wd, native_source, atombase.xdnd_enter, atombase.text_uri_list, XA_STRING); hovered_.native_wd = native_cur_wd; } - auto cur_wd = API::find_window(API::cursor_position()); + + auto cur_wd = API::find_window(API::cursor_position()); if(hovered_.window_handle != cur_wd) { _m_free_cursor(); hovered_.window_handle = cur_wd; - if((drag_wd == cur_wd) || drop_assoc_.has(drag_wd, cur_wd)) - hovered_.cursor = ::XcursorFilenameLoadCursor(disp, icons_.cursor("dnd-move").c_str()); - else - hovered_.cursor = ::XcursorFilenameLoadCursor(disp, icons_.cursor("dnd-none").c_str()); - ::XDefineCursor(disp, native_cur_wd, hovered_.cursor); + const char* icon = (((drag_wd == cur_wd) || ddrop->has(drag_wd, cur_wd)) ? "dnd-move" : "dnd-none"); + hovered_.cursor = ::XcursorFilenameLoadCursor(disp, icons_.cursor(icon).c_str()); + ::XDefineCursor(disp, native_cur_wd, hovered_.cursor); } } else if(msg_pkt.u.xevent.type == ButtonRelease) @@ -582,10 +636,6 @@ namespace nana return false; } - drop_association& drop_assoc() - { - return drop_assoc_; - } #ifdef NANA_X11 private: static nana::detail::platform_spec & _m_spec() @@ -640,13 +690,12 @@ namespace nana ::XFreeCursor(_m_spec().open_display(), hovered_.cursor); hovered_.cursor = 0; } - } #endif private: - drop_association drop_assoc_; + std::map table_; + #ifdef NANA_WINDOWS - std::map table_; #elif defined (NANA_X11) shared_icons icons_; struct hovered_status @@ -664,12 +713,20 @@ namespace nana struct simple_dragdrop::implementation { - window window_handle; + window window_handle{ nullptr }; + dragdrop_session * ddrop{ nullptr }; std::function predicate; std::map> targets; bool dragging{ false }; + struct event_handlers + { + nana::event_handle destroy; + nana::event_handle mouse_move; + nana::event_handle mouse_down; + }events; + #ifdef NANA_X11 bool cancel() { @@ -697,21 +754,26 @@ namespace nana simple_dragdrop::simple_dragdrop(window drag_wd) : impl_(new implementation) { - dragdrop_service::instance().create_dragdrop(drag_wd); - if (!API::is_window(drag_wd)) { delete impl_; throw std::invalid_argument("simple_dragdrop: invalid window handle"); } + impl_->ddrop = dragdrop_service::instance().create_dragdrop(drag_wd); + impl_->window_handle = drag_wd; API::dev::window_draggable(drag_wd, true); auto & events = API::events<>(drag_wd); #if 1 //#ifdef NANA_WINDOWS - events.mouse_down.connect_unignorable([this](const arg_mouse& arg){ + impl_->events.destroy = events.destroy.connect_unignorable([this](const arg_destroy&) { + dragdrop_service::instance().remove(impl_->window_handle); + API::dev::window_draggable(impl_->window_handle, false); + }); + + impl_->events.mouse_down = events.mouse_down.connect_unignorable([this](const arg_mouse& arg){ if (arg.is_left_button() && API::is_window(impl_->window_handle)) { impl_->dragging = ((!impl_->predicate) || impl_->predicate()); @@ -722,7 +784,7 @@ namespace nana } }); - events.mouse_move.connect_unignorable([this](const arg_mouse& arg) { + impl_->events.mouse_move = events.mouse_move.connect_unignorable([this](const arg_mouse& arg) { if (!(arg.is_left_button() && impl_->dragging && API::is_window(arg.window_handle))) return; @@ -806,19 +868,33 @@ namespace nana simple_dragdrop::~simple_dragdrop() { - dragdrop_service::instance().remove(impl_->window_handle); - API::dev::window_draggable(impl_->window_handle, false); + if (impl_->window_handle) + { + dragdrop_service::instance().remove(impl_->window_handle); + API::dev::window_draggable(impl_->window_handle, false); + + API::umake_event(impl_->events.destroy); + API::umake_event(impl_->events.mouse_down); + API::umake_event(impl_->events.mouse_move); + } + delete impl_; } void simple_dragdrop::condition(std::function predicate_fn) { + if (nullptr == impl_) + throw std::logic_error("simple_dragdrop is empty"); + impl_->predicate.swap(predicate_fn); } void simple_dragdrop::make_drop(window target, std::function drop_fn) { - dragdrop_service::instance().drop_assoc().add(impl_->window_handle, target); + if (nullptr == impl_) + throw std::logic_error("simple_dragdrop is empty"); + + impl_->ddrop->insert(impl_->window_handle, target); impl_->targets[target].swap(drop_fn); } } \ No newline at end of file From b8719f74a81e068097e6e00e86c4ee11ed08674d Mon Sep 17 00:00:00 2001 From: Katsuhisa Yuasa Date: Sun, 28 Oct 2018 21:20:19 +0900 Subject: [PATCH 067/149] Allow multiple file selection with nana::filebox on Windows platform --- include/nana/gui/filebox.hpp | 8 +++- source/gui/filebox.cpp | 85 +++++++++++++++++++++++++++++++----- 2 files changed, 81 insertions(+), 12 deletions(-) diff --git a/include/nana/gui/filebox.hpp b/include/nana/gui/filebox.hpp index f6729318..b5d4e616 100644 --- a/include/nana/gui/filebox.hpp +++ b/include/nana/gui/filebox.hpp @@ -68,8 +68,12 @@ namespace nana }; - ::std::string path() const; - ::std::string file() const; + const ::std::string& path() const; + const ::std::string& file() const; +#if defined(NANA_WINDOWS) + const ::std::vector<::std::string>& files() const; + void allow_multi_select(bool allow); +#endif /// Display the filebox dialog bool show() const; diff --git a/source/gui/filebox.cpp b/source/gui/filebox.cpp index fcaba515..765ff520 100644 --- a/source/gui/filebox.cpp +++ b/source/gui/filebox.cpp @@ -995,7 +995,12 @@ namespace nana window owner; bool open_or_save; +#if defined(NANA_WINDOWS) + bool allow_multi_select; + std::vector files; +#else std::string file; +#endif std::string title; std::string path; std::vector filters; @@ -1012,6 +1017,7 @@ namespace nana impl_->owner = owner; impl_->open_or_save = open; #if defined(NANA_WINDOWS) + impl_->allow_multi_select = false; auto len = ::GetCurrentDirectory(0, nullptr); if(len) { @@ -1068,7 +1074,11 @@ namespace nana filebox& filebox::init_file(const std::string& ifstr) { +#if defined(NANA_WINDOWS) + impl_->files = {ifstr}; +#else impl_->file = ifstr; +#endif return *this; } @@ -1079,22 +1089,41 @@ namespace nana return *this; } - std::string filebox::path() const + const std::string& filebox::path() const { return impl_->path; } - std::string filebox::file() const +#if defined(NANA_WINDOWS) + const std::string& filebox::file() const + { + if(impl_->files.empty()) + { + static const std::string empty = ""; + return empty; + } + else + { + return impl_->files.front(); + } + } + + const std::vector& filebox::files() const + { + return impl_->files; + } +#else + const std::string& filebox::file() const { return impl_->file; } +#endif bool filebox::show() const { #if defined(NANA_WINDOWS) - auto winitfile = to_wstring(impl_->file); - std::wstring wfile(winitfile); - wfile.resize(520); + std::wstring wfile(impl_->files.empty() ? L"" : to_wstring(impl_->files.front())); + wfile.resize(impl_->allow_multi_select ? (520 + 32*256) : 520); OPENFILENAME ofn; memset(&ofn, 0, sizeof ofn); @@ -1159,6 +1188,10 @@ namespace nana if (!impl_->open_or_save) ofn.Flags = OFN_OVERWRITEPROMPT; //Overwrite prompt if it is save mode ofn.Flags |= OFN_NOCHANGEDIR; + if(impl_->allow_multi_select) + { + ofn.Flags |= (OFN_ALLOWMULTISELECT | OFN_EXPLORER); + } { internal_revert_guard revert; @@ -1166,8 +1199,33 @@ namespace nana return false; } - wfile.resize(std::wcslen(wfile.data())); - impl_->file = to_utf8(wfile); + if(impl_->allow_multi_select) + { + wchar_t* str = ofn.lpstrFile; + std::wstring dir = str; + str += (dir.length() + 1); + impl_->files.clear(); + while(*str) + { + std::wstring filename = str; + std::wstring file_path = dir + L"\\" + filename; + impl_->files.emplace_back(to_utf8(file_path)); + str += (filename.length() + 1); + } + impl_->path = to_utf8(dir); + } + else + { + wfile.resize(std::wcslen(wfile.data())); + auto file = to_utf8(wfile); + impl_->files = {file}; + auto tpos = file.find_last_of("\\/"); + if(tpos != file.npos) + impl_->path = file.substr(0, tpos); + else + impl_->path.clear(); + } + #elif defined(NANA_POSIX) using mode = filebox_implement::mode; filebox_implement fb(impl_->owner, (impl_->open_or_save ? mode::open_file : mode::write_file), impl_->title); @@ -1196,16 +1254,23 @@ namespace nana API::modal_window(fb); if(false == fb.file(impl_->file)) return false; -#endif auto tpos = impl_->file.find_last_of("\\/"); if(tpos != impl_->file.npos) impl_->path = impl_->file.substr(0, tpos); else impl_->path.clear(); +#endif return true; - }//end class filebox - + } + +#if defined(NANA_WINDOWS) + void filebox::allow_multi_select(bool allow) + { + impl_->allow_multi_select = allow; + } +#endif + //end class filebox //class directory_picker struct folderbox::implement From 91675cb32d76a1e25879b942aac7f9292eed49b7 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Thu, 1 Nov 2018 06:37:26 +0800 Subject: [PATCH 068/149] improve listbox methods listbox::hovered() and listbox::insert_item() --- include/nana/gui/widgets/listbox.hpp | 13 ++++-- source/gui/widgets/listbox.cpp | 70 +++++++++++++++++++++++----- 2 files changed, 68 insertions(+), 15 deletions(-) diff --git a/include/nana/gui/widgets/listbox.hpp b/include/nana/gui/widgets/listbox.hpp index dde46edc..ae02ef38 100644 --- a/include/nana/gui/widgets/listbox.hpp +++ b/include/nana/gui/widgets/listbox.hpp @@ -1481,9 +1481,19 @@ the nana::detail::basic_window member pointer scheme */ void insert_item(const index_pair& abs_pos, const ::std::wstring& text); + + void insert_item(index_pair abs_pos, const listbox& rhs, const index_pairs& indexes); + /// Returns an index of item which contains the specified point. index_pair cast(const point & screen_pos) const; + /// Returns the item which is hovered + /** + * @param return_end Indicates whether to return an end position instead of empty position if an item is not hovered. + * @return The position of the hovered item. If return_end is true, it returns the position next to the last item of last category if an item is not hovered. + */ + index_pair hovered(bool return_end) const; + /// Returns the absolute position of column which contains the specified point. size_type column_from_pos(const point & pos) const; @@ -1497,9 +1507,6 @@ the nana::detail::basic_window member pointer scheme void erase(index_pairs indexes); ///column_size(), rhs.column_size()); + + if (0 == columns) + return; + + item_proxy it_new = this->at(abs_pos.cat).end(); + for (auto & idx : indexes) + { + auto it_src = rhs.at(idx.cat).at(idx.item); + + if (abs_pos.item < this->at(abs_pos.cat).size()) + { + this->insert_item(abs_pos, it_src.text(0)); + it_new = this->at(abs_pos); + } + else + { + it_new = this->at(abs_pos.cat).append(it_src.text(0)); + } + + for (std::size_t col = 1; col < columns; ++col) + it_new.text(col, it_src.text(col)); + + ++abs_pos.item; + } + } + listbox::cat_proxy listbox::at(size_type pos) { internal_scope_guard lock; @@ -5663,6 +5692,35 @@ namespace nana return index_pair{ npos, npos }; } + listbox::index_pair listbox::hovered(bool return_end) const + { + using parts = drawerbase::listbox::essence::parts; + + internal_scope_guard lock; + + auto cur_pos = API::cursor_position(); + API::calc_window_point(handle(), cur_pos); + + auto pt_where = _m_ess().where(cur_pos); + + if ((pt_where.first == parts::list || pt_where.first == parts::checker) && pt_where.second != npos) + { + auto pos = _m_ess().lister.advance(_m_ess().first_display(), static_cast(pt_where.second)); + if (return_end && pos.is_category()) + { + if (0 < pos.cat) + --pos.cat; + pos.item = this->size_item(pos.cat); + } + return pos; + + } + else if (return_end) + return index_pair{ this->size_categ() - 1, this->size_item(this->size_categ() - 1) }; + + return index_pair{ npos, npos }; + } + auto listbox::column_at(size_type pos, bool disp_order) -> column_interface& { internal_scope_guard lock; @@ -5852,18 +5910,6 @@ namespace nana return item_proxy(ess); } - listbox::index_pair listbox::hovered() const - { - internal_scope_guard lock; - using parts = drawerbase::listbox::essence::parts; - - auto & ptr_where = _m_ess().pointer_where; - if ((ptr_where.first == parts::list || ptr_where.first == parts::checker) && ptr_where.second != npos) - return _m_ess().lister.advance(_m_ess().first_display(), static_cast(ptr_where.second)); - - return index_pair{ npos, npos }; - } - bool listbox::sortable() const { internal_scope_guard lock; From 72f779b4bd8f41532431057d9f334bf06bb72369 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Fri, 2 Nov 2018 04:19:46 +0800 Subject: [PATCH 069/149] fix bug that content_view doesn't work correctly with dnd --- source/gui/widgets/skeletons/content_view.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/source/gui/widgets/skeletons/content_view.cpp b/source/gui/widgets/skeletons/content_view.cpp index cb7e291b..cb763b28 100644 --- a/source/gui/widgets/skeletons/content_view.cpp +++ b/source/gui/widgets/skeletons/content_view.cpp @@ -124,13 +124,16 @@ namespace nana { } else if (event_code::mouse_move == arg.evt_code) { - if (this->drag_view_move && (dragdrop_status::not_ready == API::window_dragdrop_status(this->window_handle))) + if (dragdrop_status::not_ready != API::window_dragdrop_status(this->window_handle)) { - if (this->drive(arg.pos)) - { - tmr.interval(16); - tmr.start(); - } + //When dnd is in progress, it cancels the move_view operation. + this->drag_view_move = false; + tmr.stop(); + } + else if (this->drag_view_move && this->drive(arg.pos)) + { + tmr.interval(16); + tmr.start(); } } else if (event_code::mouse_up == arg.evt_code) From d2743bb81784a0c043b1dfe216896a52ed38c54e Mon Sep 17 00:00:00 2001 From: dudztroyer Date: Sat, 3 Nov 2018 14:41:27 -0300 Subject: [PATCH 070/149] Added "select_points" method --- include/nana/gui/widgets/textbox.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/nana/gui/widgets/textbox.hpp b/include/nana/gui/widgets/textbox.hpp index 01c9a9d1..5f245761 100644 --- a/include/nana/gui/widgets/textbox.hpp +++ b/include/nana/gui/widgets/textbox.hpp @@ -224,6 +224,8 @@ namespace nana /// Selects/unselects all text. void select(bool); + void select_points(nana::upoint arg_a, nana::upoint arg_b); + /// Returns the bounds of a text selection /** * @return no selection if pair.first == pair.second. From 7651b430ebeb79b72e6183bf6e129af07550a0b7 Mon Sep 17 00:00:00 2001 From: Eduardo Roeder Date: Sat, 3 Nov 2018 14:58:53 -0300 Subject: [PATCH 071/149] Added select_points method --- include/nana/gui/widgets/skeletons/text_editor.hpp | 2 ++ source/gui/widgets/skeletons/text_editor.cpp | 13 +++++++++++-- source/gui/widgets/textbox.cpp | 11 ++++++++++- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/include/nana/gui/widgets/skeletons/text_editor.hpp b/include/nana/gui/widgets/skeletons/text_editor.hpp index dd24d365..12b23ced 100644 --- a/include/nana/gui/widgets/skeletons/text_editor.hpp +++ b/include/nana/gui/widgets/skeletons/text_editor.hpp @@ -167,6 +167,8 @@ namespace nana{ namespace widgets bool select(bool); + bool select_points(nana::upoint arg_a, nana::upoint arg_b); + /// Sets the end position of a selected string. void set_end_caret(bool stay_in_view); diff --git a/source/gui/widgets/skeletons/text_editor.cpp b/source/gui/widgets/skeletons/text_editor.cpp index ca070ace..d7a9fd4a 100644 --- a/source/gui/widgets/skeletons/text_editor.cpp +++ b/source/gui/widgets/skeletons/text_editor.cpp @@ -1845,11 +1845,11 @@ namespace nana{ namespace widgets bool text_editor::select(bool yes) { - if(yes) + if (yes) { select_.a.x = select_.a.y = 0; select_.b.y = static_cast(impl_->textbase.lines()); - if(select_.b.y) --select_.b.y; + if (select_.b.y) --select_.b.y; select_.b.x = static_cast(impl_->textbase.getline(select_.b.y).size()); select_.mode_selection = selection::mode::method_selected; impl_->try_refresh = sync_graph::refresh; @@ -1865,6 +1865,15 @@ namespace nana{ namespace widgets return false; } + bool text_editor::select_points(nana::upoint arg_a, nana::upoint arg_b) + { + select_.a = arg_a; + select_.b = arg_b; + select_.mode_selection = selection::mode::method_selected; + impl_->try_refresh = sync_graph::refresh; + return true; + } + void text_editor::set_end_caret(bool stay_in_view) { bool new_sel_end = (select_.b != points_.caret); diff --git a/source/gui/widgets/textbox.cpp b/source/gui/widgets/textbox.cpp index 96c88d6c..8695f1e5 100644 --- a/source/gui/widgets/textbox.cpp +++ b/source/gui/widgets/textbox.cpp @@ -584,7 +584,16 @@ namespace drawerbase { { internal_scope_guard lock; auto editor = get_drawer_trigger().editor(); - if(editor && editor->select(yes)) + if (editor && editor->select(yes)) + API::refresh_window(*this); + } + + + void textbox::select_points(nana::upoint arg_a, nana::upoint arg_b) + { + auto editor = get_drawer_trigger().editor(); + internal_scope_guard lock; + if (editor && editor->select_points(arg_a, arg_b)) API::refresh_window(*this); } From 64bd9b7491cb586735bb90499ef0ffc3dcde6969 Mon Sep 17 00:00:00 2001 From: Eduardo Roeder Date: Sat, 3 Nov 2018 15:43:08 -0300 Subject: [PATCH 072/149] Improve the "double click word selection" method of textbox to follow some other rules other than white space. --- source/gui/widgets/skeletons/text_editor.cpp | 6855 +++++++++--------- 1 file changed, 3431 insertions(+), 3424 deletions(-) diff --git a/source/gui/widgets/skeletons/text_editor.cpp b/source/gui/widgets/skeletons/text_editor.cpp index d7a9fd4a..304ed929 100644 --- a/source/gui/widgets/skeletons/text_editor.cpp +++ b/source/gui/widgets/skeletons/text_editor.cpp @@ -1,15 +1,15 @@ /* - * A text editor 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/skeletons/text_editor.cpp - * @contributors: Ariel Vina-Rodriguez, Oleg Smolsky - */ +* A text editor 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/skeletons/text_editor.cpp +* @contributors: Ariel Vina-Rodriguez, Oleg Smolsky +*/ #include #include #include @@ -25,637 +25,613 @@ #include #include -namespace nana{ namespace widgets -{ - namespace skeletons +namespace nana { + namespace widgets { - - template - class undoable_command_interface + namespace skeletons { - public: - virtual ~undoable_command_interface() = default; - virtual EnumCommand get() const = 0; - virtual bool merge(const undoable_command_interface&) = 0; - virtual void execute(bool redo) = 0; - }; - - template - class undoable - { - public: - using command = EnumCommand; - using container = std::deque>>; - - void clear() + template + class undoable_command_interface { - commands_.clear(); - pos_ = 0; - } + public: + virtual ~undoable_command_interface() = default; - void max_steps(std::size_t maxs) + virtual EnumCommand get() const = 0; + virtual bool merge(const undoable_command_interface&) = 0; + virtual void execute(bool redo) = 0; + }; + + template + class undoable { - max_steps_ = maxs; - if (maxs && (commands_.size() >= maxs)) - commands_.erase(commands_.begin(), commands_.begin() + (commands_.size() - maxs + 1)); - } + public: + using command = EnumCommand; + using container = std::deque>>; - std::size_t max_steps() const - { - return max_steps_; - } - - void enable(bool enb) - { - enabled_ = enb; - if (!enb) - clear(); - } - - bool enabled() const - { - return enabled_; - } - - void push(std::unique_ptr> && ptr) - { - if (!ptr || !enabled_) - return; - - if (pos_ < commands_.size()) - commands_.erase(commands_.begin() + pos_, commands_.end()); - else if (max_steps_ && (commands_.size() >= max_steps_)) - commands_.erase(commands_.begin(), commands_.begin() + (commands_.size() - max_steps_ + 1)); - - pos_ = commands_.size(); - if (!commands_.empty()) + void clear() { - if (commands_.back().get()->merge(*ptr)) + commands_.clear(); + pos_ = 0; + } + + void max_steps(std::size_t maxs) + { + max_steps_ = maxs; + if (maxs && (commands_.size() >= maxs)) + commands_.erase(commands_.begin(), commands_.begin() + (commands_.size() - maxs + 1)); + } + + std::size_t max_steps() const + { + return max_steps_; + } + + void enable(bool enb) + { + enabled_ = enb; + if (!enb) + clear(); + } + + bool enabled() const + { + return enabled_; + } + + void push(std::unique_ptr> && ptr) + { + if (!ptr || !enabled_) return; + + if (pos_ < commands_.size()) + commands_.erase(commands_.begin() + pos_, commands_.end()); + else if (max_steps_ && (commands_.size() >= max_steps_)) + commands_.erase(commands_.begin(), commands_.begin() + (commands_.size() - max_steps_ + 1)); + + pos_ = commands_.size(); + if (!commands_.empty()) + { + if (commands_.back().get()->merge(*ptr)) + return; + } + + commands_.emplace_back(std::move(ptr)); + ++pos_; } - commands_.emplace_back(std::move(ptr)); - ++pos_; - } - - std::size_t count(bool is_undo) const - { - return (is_undo ? pos_ : commands_.size() - pos_); - } - - void undo() - { - if (pos_ > 0) + std::size_t count(bool is_undo) const { - --pos_; - commands_[pos_].get()->execute(false); + return (is_undo ? pos_ : commands_.size() - pos_); } - } - void redo() - { - if (pos_ != commands_.size()) - commands_[pos_++].get()->execute(true); - } - - private: - container commands_; - bool enabled_{ true }; - std::size_t max_steps_{ 30 }; - std::size_t pos_{ 0 }; - }; - - template - using undo_command_ptr = std::unique_ptr > ; - - template - class text_editor::basic_undoable - : public undoable_command_interface - { - public: - basic_undoable(text_editor& te, EnumCommand cmd) - : editor_(te), cmd_(cmd) - {} - - void set_selected_text() - { - //sel_a_ and sel_b_ are not sorted, sel_b_ keeps the caret position. - sel_a_ = editor_.select_.a; - sel_b_ = editor_.select_.b; - - if (sel_a_ != sel_b_) + void undo() { - selected_text_ = editor_._m_make_select_string(); + if (pos_ > 0) + { + --pos_; + commands_[pos_].get()->execute(false); + } } - } - void set_caret_pos() - { - pos_ = editor_.caret(); - } - protected: - EnumCommand get() const override - { - return cmd_; - } - - virtual bool merge(const undoable_command_interface&) override - { - return false; - } - protected: - text_editor & editor_; - upoint pos_; - upoint sel_a_, sel_b_; - std::wstring selected_text_; - private: - const EnumCommand cmd_; - }; - - class text_editor::undo_backspace - : public basic_undoable < command > - { - public: - undo_backspace(text_editor& editor) - : basic_undoable(editor, command::backspace) - { - } - - void set_removed(std::wstring str) - { - selected_text_ = std::move(str); - } - - void execute(bool redo) override - { - editor_._m_cancel_select(0); - editor_.points_.caret = pos_; - - bool is_enter = ((selected_text_.size() == 1) && ('\n' == selected_text_[0])); - if (redo) + void redo() { + if (pos_ != commands_.size()) + commands_[pos_++].get()->execute(true); + } + + private: + container commands_; + bool enabled_{ true }; + std::size_t max_steps_{ 30 }; + std::size_t pos_{ 0 }; + }; + + template + using undo_command_ptr = std::unique_ptr >; + + template + class text_editor::basic_undoable + : public undoable_command_interface + { + public: + basic_undoable(text_editor& te, EnumCommand cmd) + : editor_(te), cmd_(cmd) + {} + + void set_selected_text() + { + //sel_a_ and sel_b_ are not sorted, sel_b_ keeps the caret position. + sel_a_ = editor_.select_.a; + sel_b_ = editor_.select_.b; + if (sel_a_ != sel_b_) { - editor_.select_.a = sel_a_; - editor_.select_.b = sel_b_; - editor_._m_erase_select(false); - editor_.select_.a = editor_.select_.b; - editor_.points_.caret = sel_a_; + selected_text_ = editor_._m_make_select_string(); + } + } + + void set_caret_pos() + { + pos_ = editor_.caret(); + } + protected: + EnumCommand get() const override + { + return cmd_; + } + + virtual bool merge(const undoable_command_interface&) override + { + return false; + } + protected: + text_editor & editor_; + upoint pos_; + upoint sel_a_, sel_b_; + std::wstring selected_text_; + private: + const EnumCommand cmd_; + }; + + class text_editor::undo_backspace + : public basic_undoable < command > + { + public: + undo_backspace(text_editor& editor) + : basic_undoable(editor, command::backspace) + { + } + + void set_removed(std::wstring str) + { + selected_text_ = std::move(str); + } + + void execute(bool redo) override + { + editor_._m_cancel_select(0); + editor_.points_.caret = pos_; + + bool is_enter = ((selected_text_.size() == 1) && ('\n' == selected_text_[0])); + if (redo) + { + if (sel_a_ != sel_b_) + { + editor_.select_.a = sel_a_; + editor_.select_.b = sel_b_; + editor_._m_erase_select(false); + editor_.select_.a = editor_.select_.b; + editor_.points_.caret = sel_a_; + } + else + { + if (is_enter) + { + editor_.points_.caret = nana::upoint(0, pos_.y + 1); + editor_.backspace(false, false); + } + else + editor_.textbase().erase(pos_.y, pos_.x, selected_text_.size()); + } } else { if (is_enter) { - editor_.points_.caret = nana::upoint(0, pos_.y + 1); - editor_.backspace(false, false); + editor_.enter(false, false); } else - editor_.textbase().erase(pos_.y, pos_.x, selected_text_.size()); - } - } - else - { - if (is_enter) - { - editor_.enter(false, false); - } - else - { - editor_._m_put(selected_text_, false); - if (sel_a_ != sel_b_) { - editor_.select_.a = sel_a_; - editor_.select_.b = sel_b_; - editor_.points_.caret = sel_b_; + editor_._m_put(selected_text_, false); + if (sel_a_ != sel_b_) + { + editor_.select_.a = sel_a_; + editor_.select_.b = sel_b_; + editor_.points_.caret = sel_b_; + } + else + ++editor_.points_.caret.x; } - else - ++editor_.points_.caret.x; - } - } - - editor_.textbase().text_changed(); - - editor_.reset_caret(); - } - }; - - class text_editor::undo_input_text - : public basic_undoable - { - public: - undo_input_text(text_editor & editor, const std::wstring& text) - : basic_undoable(editor, command::input_text), - text_(text) - { - } - - void execute(bool redo) override - { - bool is_enter = (text_.size() == 1 && '\n' == text_[0]); - editor_._m_cancel_select(0); - editor_.points_.caret = pos_; //The pos_ specifies the caret position before input - - if (redo) - { - if (is_enter) - { - editor_.enter(false, false); - } - else - { - if (!selected_text_.empty()) - { - editor_.select_.a = sel_a_; - editor_.select_.b = sel_b_; - editor_._m_erase_select(false); - } - editor_.points_.caret = editor_._m_put(text_, false); //redo - } - } - else - { - if (is_enter) - { - editor_.points_.caret.x = 0; - ++editor_.points_.caret.y; - editor_.backspace(false, false); - } - else - { - std::vector> lines; - if (editor_._m_resolve_text(text_, lines)) - { - editor_.select_.a = pos_; - editor_.select_.b = upoint(static_cast(lines.back().second - lines.back().first), static_cast(pos_.y + lines.size() - 1)); - editor_.backspace(false, false); - editor_.select_.a = editor_.select_.b; - } - else - editor_.textbase().erase(pos_.y, pos_.x, text_.size()); //undo } - if (!selected_text_.empty()) - { - editor_.points_.caret = (std::min)(sel_a_, sel_b_); - editor_._m_put(selected_text_, false); - editor_.points_.caret = sel_b_; - editor_.select_.a = sel_a_; //Reset the selected text - editor_.select_.b = sel_b_; - } - } + editor_.textbase().text_changed(); - editor_.textbase().text_changed(); - editor_.reset_caret(); - } - private: - std::wstring text_; - }; - - class text_editor::undo_move_text - : public basic_undoable - { - public: - undo_move_text(text_editor& editor) - : basic_undoable(editor, command::move_text) - {} - - void execute(bool redo) override - { - if (redo) - { - editor_.select_.a = sel_a_; - editor_.select_.b = sel_b_; - editor_.points_.caret = pos_; - editor_._m_move_select(false); - } - else - { - editor_.select_.a = dest_a_; - editor_.select_.b = dest_b_; - editor_.points_.caret = (sel_a_ < sel_b_ ? sel_a_ : sel_b_); - - const auto text = editor_._m_make_select_string(); - - editor_._m_erase_select(false); - editor_._m_put(text, false); - - editor_.select_.a = sel_a_; - editor_.select_.b = sel_b_; - - editor_.points_.caret = sel_b_; editor_.reset_caret(); } - editor_.textbase().text_changed(); - } + }; - void set_destination(const nana::upoint& dest_a, const nana::upoint& dest_b) + class text_editor::undo_input_text + : public basic_undoable { - dest_a_ = dest_a; - dest_b_ = dest_b; - } - private: - nana::upoint dest_a_, dest_b_; - }; - - struct text_editor::text_section - { - const wchar_t* begin{ nullptr }; - const wchar_t* end{ nullptr }; - unsigned pixels{ 0 }; - - text_section() = default; - text_section(const wchar_t* ptr, const wchar_t* endptr, unsigned px) - : begin(ptr), end(endptr), pixels(px) - {} - }; - - - struct keyword_scheme - { - ::nana::color fgcolor; - ::nana::color bgcolor; - }; - - struct keyword_desc - { - std::wstring text; - std::string scheme; - bool case_sensitive; - bool whole_word_matched; - - keyword_desc(const std::wstring& txt, const std::string& schm, bool cs, bool wwm) - : text(txt), scheme(schm), case_sensitive(cs), whole_word_matched(wwm) - {} - }; - - struct entity - { - const wchar_t* begin; - const wchar_t* end; - const keyword_scheme * scheme; - }; - - enum class sync_graph - { - none, - refresh, - lazy_refresh - }; - - colored_area_access_interface::~colored_area_access_interface(){} - - class colored_area_access - : public colored_area_access_interface - { - public: - void set_window(window handle) - { - window_handle_ = handle; - } - - std::shared_ptr find(std::size_t pos) const - { - for (auto & sp : colored_areas_) + public: + undo_input_text(text_editor & editor, const std::wstring& text) + : basic_undoable(editor, command::input_text), + text_(text) { - if (sp->begin <= pos && pos < sp->begin + sp->count) - return sp; - else if (sp->begin > pos) - break; - } - return{}; - } - public: - //Overrides methods of colored_area_access_interface - std::shared_ptr get(std::size_t line_pos) override - { -#ifdef _MSC_VER - auto i = colored_areas_.cbegin(); - for (; i != colored_areas_.cend(); ++i) -#else - auto i = colored_areas_.begin(); - for (; i != colored_areas_.end(); ++i) -#endif - { - auto & area = *(i->get()); - if (area.begin <= line_pos && line_pos < area.begin + area.count) - return *i; - - if (area.begin > line_pos) - break; } - return *colored_areas_.emplace(i, - std::make_shared(colored_area_type{line_pos, 1, color{}, color{}}) - ); - } - - bool clear() override - { - if (colored_areas_.empty()) - return false; - - colored_areas_.clear(); - API::refresh_window(window_handle_); - return true; - } - - bool remove(std::size_t pos) override - { - bool changed = false; -#ifdef _MSC_VER - for (auto i = colored_areas_.cbegin(); i != colored_areas_.cend();) -#else - for (auto i = colored_areas_.begin(); i != colored_areas_.end();) -#endif + void execute(bool redo) override { - if (i->get()->begin <= pos && pos < i->get()->begin + i->get()->count) + bool is_enter = (text_.size() == 1 && '\n' == text_[0]); + editor_._m_cancel_select(0); + editor_.points_.caret = pos_; //The pos_ specifies the caret position before input + + if (redo) { - i = colored_areas_.erase(i); - changed = true; + if (is_enter) + { + editor_.enter(false, false); + } + else + { + if (!selected_text_.empty()) + { + editor_.select_.a = sel_a_; + editor_.select_.b = sel_b_; + editor_._m_erase_select(false); + } + editor_.points_.caret = editor_._m_put(text_, false); //redo + } } - else if (i->get()->begin > pos) - break; + else + { + if (is_enter) + { + editor_.points_.caret.x = 0; + ++editor_.points_.caret.y; + editor_.backspace(false, false); + } + else + { + std::vector> lines; + if (editor_._m_resolve_text(text_, lines)) + { + editor_.select_.a = pos_; + editor_.select_.b = upoint(static_cast(lines.back().second - lines.back().first), static_cast(pos_.y + lines.size() - 1)); + editor_.backspace(false, false); + editor_.select_.a = editor_.select_.b; + } + else + editor_.textbase().erase(pos_.y, pos_.x, text_.size()); //undo + } + + if (!selected_text_.empty()) + { + editor_.points_.caret = (std::min)(sel_a_, sel_b_); + editor_._m_put(selected_text_, false); + editor_.points_.caret = sel_b_; + editor_.select_.a = sel_a_; //Reset the selected text + editor_.select_.b = sel_b_; + } + } + + editor_.textbase().text_changed(); + editor_.reset_caret(); } - if (changed) - API::refresh_window(window_handle_); + private: + std::wstring text_; + }; - return changed; - } - - std::size_t size() const override + class text_editor::undo_move_text + : public basic_undoable { - return colored_areas_.size(); - } + public: + undo_move_text(text_editor& editor) + : basic_undoable(editor, command::move_text) + {} - std::shared_ptr at(std::size_t index) override + void execute(bool redo) override + { + if (redo) + { + editor_.select_.a = sel_a_; + editor_.select_.b = sel_b_; + editor_.points_.caret = pos_; + editor_._m_move_select(false); + } + else + { + editor_.select_.a = dest_a_; + editor_.select_.b = dest_b_; + editor_.points_.caret = (sel_a_ < sel_b_ ? sel_a_ : sel_b_); + + const auto text = editor_._m_make_select_string(); + + editor_._m_erase_select(false); + editor_._m_put(text, false); + + editor_.select_.a = sel_a_; + editor_.select_.b = sel_b_; + + editor_.points_.caret = sel_b_; + editor_.reset_caret(); + } + editor_.textbase().text_changed(); + } + + void set_destination(const nana::upoint& dest_a, const nana::upoint& dest_b) + { + dest_a_ = dest_a; + dest_b_ = dest_b; + } + private: + nana::upoint dest_a_, dest_b_; + }; + + struct text_editor::text_section { - return colored_areas_.at(index); - } - private: - window window_handle_; - std::vector> colored_areas_; - }; + const wchar_t* begin{ nullptr }; + const wchar_t* end{ nullptr }; + unsigned pixels{ 0 }; - struct text_editor::implementation - { - undoable undo; //undo command - renderers customized_renderers; - std::vector text_position; //positions of text since last rendering. - int text_position_origin{ -1 }; //origin when last text_exposed + text_section() = default; + text_section(const wchar_t* ptr, const wchar_t* endptr, unsigned px) + : begin(ptr), end(endptr), pixels(px) + {} + }; - skeletons::textbase textbase; - sync_graph try_refresh{ sync_graph::none }; - - colored_area_access colored_area; - - struct inner_capacities + struct keyword_scheme { - editor_behavior_interface * behavior; + ::nana::color fgcolor; + ::nana::color bgcolor; + }; - accepts acceptive{ accepts::no_restrict }; - std::function pred_acceptive; - }capacities; - - struct inner_counterpart + struct keyword_desc { - bool enabled{ false }; - paint::graphics buffer; //A offscreen buffer which keeps the background that painted by external part. - }counterpart; + std::wstring text; + std::string scheme; + bool case_sensitive; + bool whole_word_matched; - struct indent_rep + keyword_desc(const std::wstring& txt, const std::string& schm, bool cs, bool wwm) + : text(txt), scheme(schm), case_sensitive(cs), whole_word_matched(wwm) + {} + }; + + struct entity { - bool enabled{ false }; - std::function generator; - }indent; + const wchar_t* begin; + const wchar_t* end; + const keyword_scheme * scheme; + }; - struct inner_keywords + enum class sync_graph { - std::map> schemes; - std::deque base; - }keywords; + none, + refresh, + lazy_refresh + }; - std::unique_ptr cview; - }; + colored_area_access_interface::~colored_area_access_interface() {} - - class text_editor::editor_behavior_interface - { - public: - using row_coordinate = std::pair; ///< A coordinate type for line position. first: the absolute line position of text. second: the secondary line position of a part of line. - - virtual ~editor_behavior_interface() = default; - - /// Returns the text sections of a specified line - /** - * @param pos The absolute line number. - * @return The text sections of this line. - */ - virtual std::vector line(std::size_t pos) const = 0; - virtual row_coordinate text_position_from_screen(int top) const = 0; - - virtual unsigned max_pixels() const = 0; - - /// Deletes lines between first and second, and then, second line will be merged into first line. - virtual void merge_lines(std::size_t first, std::size_t second) = 0; - //Calculates how many lines the specified line of text takes with a specified pixels of width. - virtual void add_lines(std::size_t pos, std::size_t lines) = 0; - virtual void prepare() = 0; - virtual void pre_calc_line(std::size_t line, unsigned pixels) = 0; - virtual void pre_calc_lines(unsigned pixels) = 0; - virtual std::size_t take_lines() const = 0; - /// Returns the number of lines that the line of text specified by pos takes. - virtual std::size_t take_lines(std::size_t pos) const = 0; - }; - - inline bool is_right_text(const unicode_bidi::entity& e) - { - return ((e.bidi_char_type != unicode_bidi::bidi_char::L) && (e.level & 1)); - } - - - class text_editor::behavior_normal - : public editor_behavior_interface - { - public: - behavior_normal(text_editor& editor) - : editor_(editor) - {} - - std::vector line(std::size_t pos) const override + class colored_area_access + : public colored_area_access_interface { - //Every line of normal behavior only has one text_section - std::vector sections; - sections.emplace_back(this->sections_[pos]); - return sections; - } + public: + void set_window(window handle) + { + window_handle_ = handle; + } - row_coordinate text_position_from_screen(int top) const override - { - const std::size_t textlines = editor_.textbase().lines(); - const auto line_px = static_cast(editor_.line_height()); - if ((0 == textlines) || (0 == line_px)) + std::shared_ptr find(std::size_t pos) const + { + for (auto & sp : colored_areas_) + { + if (sp->begin <= pos && pos < sp->begin + sp->count) + return sp; + else if (sp->begin > pos) + break; + } return{}; - - if (top < editor_.text_area_.area.y) - top = (std::max)(editor_._m_text_topline() - 1, 0); - else - top = (top - editor_.text_area_.area.y + editor_.impl_->cview->origin().y) / line_px; - - return{ (textlines <= static_cast(top) ? textlines - 1 : static_cast(top)), - 0 }; - } - - unsigned max_pixels() const override - { - unsigned px = editor_.width_pixels(); - for (auto & sct : sections_) - { - if (sct.pixels > px) - px = sct.pixels; } - return px; - } - - void merge_lines(std::size_t first, std::size_t second) override - { - if (first > second) - std::swap(first, second); - - if (second < this->sections_.size()) -#ifdef _MSC_VER - this->sections_.erase(this->sections_.cbegin() + (first + 1), this->sections_.cbegin() + second); -#else - this->sections_.erase(this->sections_.begin() + (first + 1), this->sections_.begin() + second); -#endif - pre_calc_line(first, 0); - - //textbase is implement by using deque, and the linemtr holds the text pointers - //If the textbase is changed, it will check the text pointers. - std::size_t line = 0; - - auto const & const_sections = sections_; - for (auto & sct : const_sections) + public: + //Overrides methods of colored_area_access_interface + std::shared_ptr get(std::size_t line_pos) override { - auto const& text = editor_.textbase().getline(line); - if (sct.begin < text.c_str() || (text.c_str() + text.size() < sct.begin)) - pre_calc_line(line, 0); +#ifdef _MSC_VER + auto i = colored_areas_.cbegin(); + for (; i != colored_areas_.cend(); ++i) +#else + auto i = colored_areas_.begin(); + for (; i != colored_areas_.end(); ++i) +#endif + { + auto & area = *(i->get()); + if (area.begin <= line_pos && line_pos < area.begin + area.count) + return *i; - ++line; + if (area.begin > line_pos) + break; + } + + return *colored_areas_.emplace(i, + std::make_shared(colored_area_type{ line_pos, 1, color{}, color{} }) + ); } + + bool clear() override + { + if (colored_areas_.empty()) + return false; + + colored_areas_.clear(); + API::refresh_window(window_handle_); + return true; + } + + bool remove(std::size_t pos) override + { + bool changed = false; +#ifdef _MSC_VER + for (auto i = colored_areas_.cbegin(); i != colored_areas_.cend();) +#else + for (auto i = colored_areas_.begin(); i != colored_areas_.end();) +#endif + { + if (i->get()->begin <= pos && pos < i->get()->begin + i->get()->count) + { + i = colored_areas_.erase(i); + changed = true; + } + else if (i->get()->begin > pos) + break; + } + if (changed) + API::refresh_window(window_handle_); + + return changed; + } + + std::size_t size() const override + { + return colored_areas_.size(); + } + + std::shared_ptr at(std::size_t index) override + { + return colored_areas_.at(index); + } + private: + window window_handle_; + std::vector> colored_areas_; + }; + + struct text_editor::implementation + { + undoable undo; //undo command + renderers customized_renderers; + std::vector text_position; //positions of text since last rendering. + int text_position_origin{ -1 }; //origin when last text_exposed + + skeletons::textbase textbase; + + sync_graph try_refresh{ sync_graph::none }; + + colored_area_access colored_area; + + struct inner_capacities + { + editor_behavior_interface * behavior; + + accepts acceptive{ accepts::no_restrict }; + std::function pred_acceptive; + }capacities; + + struct inner_counterpart + { + bool enabled{ false }; + paint::graphics buffer; //A offscreen buffer which keeps the background that painted by external part. + }counterpart; + + struct indent_rep + { + bool enabled{ false }; + std::function generator; + }indent; + + struct inner_keywords + { + std::map> schemes; + std::deque base; + }keywords; + + std::unique_ptr cview; + }; + + + class text_editor::editor_behavior_interface + { + public: + using row_coordinate = std::pair; ///< A coordinate type for line position. first: the absolute line position of text. second: the secondary line position of a part of line. + + virtual ~editor_behavior_interface() = default; + + /// Returns the text sections of a specified line + /** + * @param pos The absolute line number. + * @return The text sections of this line. + */ + virtual std::vector line(std::size_t pos) const = 0; + virtual row_coordinate text_position_from_screen(int top) const = 0; + + virtual unsigned max_pixels() const = 0; + + /// Deletes lines between first and second, and then, second line will be merged into first line. + virtual void merge_lines(std::size_t first, std::size_t second) = 0; + //Calculates how many lines the specified line of text takes with a specified pixels of width. + virtual void add_lines(std::size_t pos, std::size_t lines) = 0; + virtual void prepare() = 0; + virtual void pre_calc_line(std::size_t line, unsigned pixels) = 0; + virtual void pre_calc_lines(unsigned pixels) = 0; + virtual std::size_t take_lines() const = 0; + /// Returns the number of lines that the line of text specified by pos takes. + virtual std::size_t take_lines(std::size_t pos) const = 0; + }; + + inline bool is_right_text(const unicode_bidi::entity& e) + { + return ((e.bidi_char_type != unicode_bidi::bidi_char::L) && (e.level & 1)); } - void add_lines(std::size_t pos, std::size_t line_size) override + + class text_editor::behavior_normal + : public editor_behavior_interface { - if (pos < this->sections_.size()) + public: + behavior_normal(text_editor& editor) + : editor_(editor) + {} + + std::vector line(std::size_t pos) const override { - for (std::size_t i = 0; i < line_size; ++i) + //Every line of normal behavior only has one text_section + std::vector sections; + sections.emplace_back(this->sections_[pos]); + return sections; + } + + row_coordinate text_position_from_screen(int top) const override + { + const std::size_t textlines = editor_.textbase().lines(); + const auto line_px = static_cast(editor_.line_height()); + if ((0 == textlines) || (0 == line_px)) + return{}; + + if (top < editor_.text_area_.area.y) + top = (std::max)(editor_._m_text_topline() - 1, 0); + else + top = (top - editor_.text_area_.area.y + editor_.impl_->cview->origin().y) / line_px; + + return{ (textlines <= static_cast(top) ? textlines - 1 : static_cast(top)), + 0 }; + } + + unsigned max_pixels() const override + { + unsigned px = editor_.width_pixels(); + for (auto & sct : sections_) + { + if (sct.pixels > px) + px = sct.pixels; + } + return px; + } + + void merge_lines(std::size_t first, std::size_t second) override + { + if (first > second) + std::swap(first, second); + + if (second < this->sections_.size()) #ifdef _MSC_VER - this->sections_.emplace(this->sections_.cbegin() + (pos + i)); + this->sections_.erase(this->sections_.cbegin() + (first + 1), this->sections_.cbegin() + second); #else - this->sections_.emplace(this->sections_.begin() + (pos + i)); + this->sections_.erase(this->sections_.begin() + (first + 1), this->sections_.begin() + second); #endif + pre_calc_line(first, 0); + //textbase is implement by using deque, and the linemtr holds the text pointers //If the textbase is changed, it will check the text pointers. std::size_t line = 0; @@ -663,3142 +639,3173 @@ namespace nana{ namespace widgets auto const & const_sections = sections_; for (auto & sct : const_sections) { - if (line < pos || (pos + line_size) <= line) - { - auto const & text = editor_.textbase().getline(line); - if (sct.begin < text.c_str() || (text.c_str() + text.size() < sct.begin)) - pre_calc_line(line, 0); - } + auto const& text = editor_.textbase().getline(line); + if (sct.begin < text.c_str() || (text.c_str() + text.size() < sct.begin)) + pre_calc_line(line, 0); + ++line; } } - } - void prepare() override + void add_lines(std::size_t pos, std::size_t line_size) override + { + if (pos < this->sections_.size()) + { + for (std::size_t i = 0; i < line_size; ++i) +#ifdef _MSC_VER + this->sections_.emplace(this->sections_.cbegin() + (pos + i)); +#else + this->sections_.emplace(this->sections_.begin() + (pos + i)); +#endif + //textbase is implement by using deque, and the linemtr holds the text pointers + //If the textbase is changed, it will check the text pointers. + std::size_t line = 0; + + auto const & const_sections = sections_; + for (auto & sct : const_sections) + { + if (line < pos || (pos + line_size) <= line) + { + auto const & text = editor_.textbase().getline(line); + if (sct.begin < text.c_str() || (text.c_str() + text.size() < sct.begin)) + pre_calc_line(line, 0); + } + ++line; + } + } + } + + void prepare() override + { + auto const line_count = editor_.textbase().lines(); + this->sections_.resize(line_count); + } + + void pre_calc_line(std::size_t pos, unsigned) override + { + auto const & text = editor_.textbase().getline(pos); + auto& txt_section = this->sections_[pos]; + txt_section.begin = text.c_str(); + txt_section.end = txt_section.begin + text.size(); + txt_section.pixels = editor_._m_text_extent_size(txt_section.begin, text.size()).width; + } + + void pre_calc_lines(unsigned) override + { + auto const line_count = editor_.textbase().lines(); + this->sections_.resize(line_count); + for (std::size_t i = 0; i < line_count; ++i) + pre_calc_line(i, 0); + } + + std::size_t take_lines() const override + { + return editor_.textbase().lines(); + } + + std::size_t take_lines(std::size_t) const override + { + return 1; + } + private: + text_editor& editor_; + std::vector sections_; + }; //end class behavior_normal + + + class text_editor::behavior_linewrapped + : public text_editor::editor_behavior_interface { - auto const line_count = editor_.textbase().lines(); - this->sections_.resize(line_count); - } + struct line_metrics + { + std::size_t take_lines; //The number of lines that text of this line takes. + std::vector line_sections; + }; + public: + behavior_linewrapped(text_editor& editor) + : editor_(editor) + {} - void pre_calc_line(std::size_t pos, unsigned) override - { - auto const & text = editor_.textbase().getline(pos); - auto& txt_section = this->sections_[pos]; - txt_section.begin = text.c_str(); - txt_section.end = txt_section.begin + text.size(); - txt_section.pixels = editor_._m_text_extent_size(txt_section.begin, text.size()).width; - } + std::vector line(std::size_t pos) const override + { + return linemtr_[pos].line_sections; + } - void pre_calc_lines(unsigned) override - { - auto const line_count = editor_.textbase().lines(); - this->sections_.resize(line_count); - for (std::size_t i = 0; i < line_count; ++i) - pre_calc_line(i, 0); - } + row_coordinate text_position_from_screen(int top) const override + { + row_coordinate coord; + const auto line_px = static_cast(editor_.line_height()); - std::size_t take_lines() const override - { - return editor_.textbase().lines(); - } + if ((0 == editor_.textbase().lines()) || (0 == line_px)) + return coord; - std::size_t take_lines(std::size_t) const override - { - return 1; - } - private: - text_editor& editor_; - std::vector sections_; - }; //end class behavior_normal + auto text_row = (std::max)(0, (top - editor_.text_area_.area.y + editor_.impl_->cview->origin().y) / line_px); - - class text_editor::behavior_linewrapped - : public text_editor::editor_behavior_interface - { - struct line_metrics - { - std::size_t take_lines; //The number of lines that text of this line takes. - std::vector line_sections; - }; - public: - behavior_linewrapped(text_editor& editor) - : editor_(editor) - {} - - std::vector line(std::size_t pos) const override - { - return linemtr_[pos].line_sections; - } - - row_coordinate text_position_from_screen(int top) const override - { - row_coordinate coord; - const auto line_px = static_cast(editor_.line_height()); - - if ((0 == editor_.textbase().lines()) || (0 == line_px)) + coord = _m_textline(static_cast(text_row)); + if (linemtr_.size() <= coord.first) + { + coord.first = linemtr_.size() - 1; + coord.second = linemtr_.back().line_sections.size() - 1; + } return coord; - - auto text_row = (std::max)(0, (top - editor_.text_area_.area.y + editor_.impl_->cview->origin().y) / line_px); - - coord = _m_textline(static_cast(text_row)); - if (linemtr_.size() <= coord.first) - { - coord.first = linemtr_.size() - 1; - coord.second = linemtr_.back().line_sections.size() - 1; } - return coord; - } - unsigned max_pixels() const override - { - return editor_.width_pixels(); - } - - void merge_lines(std::size_t first, std::size_t second) override - { - if (first > second) - std::swap(first, second); - - if (second < linemtr_.size()) - linemtr_.erase(linemtr_.begin() + first + 1, linemtr_.begin() + second + 1); - - auto const width_px = editor_.width_pixels(); - - pre_calc_line(first, width_px); - } - - void add_lines(std::size_t pos, std::size_t lines) override - { - if (pos < linemtr_.size()) + unsigned max_pixels() const override { - for (std::size_t i = 0; i < lines; ++i) - linemtr_.emplace(linemtr_.begin() + pos + i); + return editor_.width_pixels(); + } - //textbase is implement by using deque, and the linemtr holds the text pointers - //If the textbase is changed, it will check the text pointers. - std::size_t line = 0; + void merge_lines(std::size_t first, std::size_t second) override + { + if (first > second) + std::swap(first, second); - auto const & const_linemtr = linemtr_; - for (auto & mtr : const_linemtr) + if (second < linemtr_.size()) + linemtr_.erase(linemtr_.begin() + first + 1, linemtr_.begin() + second + 1); + + auto const width_px = editor_.width_pixels(); + + pre_calc_line(first, width_px); + } + + void add_lines(std::size_t pos, std::size_t lines) override + { + if (pos < linemtr_.size()) { - if (line < pos || (pos + lines) <= line) + for (std::size_t i = 0; i < lines; ++i) + linemtr_.emplace(linemtr_.begin() + pos + i); + + //textbase is implement by using deque, and the linemtr holds the text pointers + //If the textbase is changed, it will check the text pointers. + std::size_t line = 0; + + auto const & const_linemtr = linemtr_; + for (auto & mtr : const_linemtr) { - auto & linestr = editor_.textbase().getline(line); - auto p = mtr.line_sections.front().begin; - if (p < linestr.c_str() || (linestr.c_str() + linestr.size() < p)) - pre_calc_line(line, editor_.width_pixels()); + if (line < pos || (pos + lines) <= line) + { + auto & linestr = editor_.textbase().getline(line); + auto p = mtr.line_sections.front().begin; + if (p < linestr.c_str() || (linestr.c_str() + linestr.size() < p)) + pre_calc_line(line, editor_.width_pixels()); + } + ++line; } - ++line; } } - } - void prepare() override - { - auto const lines = editor_.textbase().lines(); - linemtr_.resize(lines); - } - - void pre_calc_line(std::size_t line, unsigned pixels) override - { - const string_type& lnstr = editor_.textbase().getline(line); - if (lnstr.empty()) + void prepare() override { - auto & mtr = linemtr_[line]; - mtr.line_sections.clear(); - - mtr.line_sections.emplace_back(lnstr.c_str(), lnstr.c_str(), unsigned{}); - mtr.take_lines = 1; - return; + auto const lines = editor_.textbase().lines(); + linemtr_.resize(lines); } - std::vector sections; - _m_text_section(lnstr, sections); - - std::vector line_sections; - - unsigned text_px = 0; - const wchar_t * secondary_begin = nullptr; - for (auto & ts : sections) + void pre_calc_line(std::size_t line, unsigned pixels) override { - if (!secondary_begin) - secondary_begin = ts.begin; - - const unsigned str_w = editor_._m_text_extent_size(ts.begin, ts.end - ts.begin).width; - - text_px += str_w; - if (text_px >= pixels) + const string_type& lnstr = editor_.textbase().getline(line); + if (lnstr.empty()) { - if (text_px != str_w) + auto & mtr = linemtr_[line]; + mtr.line_sections.clear(); + + mtr.line_sections.emplace_back(lnstr.c_str(), lnstr.c_str(), unsigned{}); + mtr.take_lines = 1; + return; + } + + std::vector sections; + _m_text_section(lnstr, sections); + + std::vector line_sections; + + unsigned text_px = 0; + const wchar_t * secondary_begin = nullptr; + for (auto & ts : sections) + { + if (!secondary_begin) + secondary_begin = ts.begin; + + const unsigned str_w = editor_._m_text_extent_size(ts.begin, ts.end - ts.begin).width; + + text_px += str_w; + if (text_px >= pixels) + { + if (text_px != str_w) + { + line_sections.emplace_back(secondary_begin, ts.begin, unsigned{ text_px - str_w }); + text_px = str_w; + secondary_begin = ts.begin; + } + + if (str_w > pixels) //Indicates the splitting of ts string + { + std::size_t len = ts.end - ts.begin; +#ifdef _nana_std_has_string_view + auto pxbuf = editor_.graph_.glyph_pixels({ ts.begin, len }); +#else + std::unique_ptr pxbuf(new unsigned[len]); + editor_.graph_.glyph_pixels(ts.begin, len, pxbuf.get()); +#endif + + auto pxptr = pxbuf.get(); + auto pxend = pxptr + len; + + secondary_begin = ts.begin; + text_px = 0; + for (auto pxi = pxptr; pxi != pxend; ++pxi) + { + text_px += *pxi; + if (text_px < pixels) + continue; + + const wchar_t * endptr = ts.begin + (pxi - pxptr) + (text_px == pixels ? 1 : 0); + line_sections.emplace_back(secondary_begin, endptr, unsigned{ text_px - (text_px == pixels ? 0 : *pxi) }); + secondary_begin = endptr; + + text_px = (text_px == pixels ? 0 : *pxi); + } + } + continue; + } + else if (text_px == pixels) { line_sections.emplace_back(secondary_begin, ts.begin, unsigned{ text_px - str_w }); - text_px = str_w; secondary_begin = ts.begin; + text_px = str_w; + } + } + + auto & mtr = linemtr_[line]; + + mtr.take_lines = line_sections.size(); + mtr.line_sections.swap(line_sections); + + if (secondary_begin) + { + mtr.line_sections.emplace_back(secondary_begin, sections.back().end, unsigned{ text_px }); + ++mtr.take_lines; + } + } + + void pre_calc_lines(unsigned pixels) override + { + auto const lines = editor_.textbase().lines(); + linemtr_.resize(lines); + + for (std::size_t i = 0; i < lines; ++i) + pre_calc_line(i, pixels); + } + + std::size_t take_lines() const override + { + std::size_t lines = 0; + for (auto & mtr : linemtr_) + lines += mtr.take_lines; + + return lines; + } + + std::size_t take_lines(std::size_t pos) const override + { + return (pos < linemtr_.size() ? linemtr_[pos].take_lines : 0); + } + private: + /// Split a text into multiple sections, a section indicates an english word or a CKJ character + void _m_text_section(const std::wstring& str, std::vector& tsec) + { + if (str.empty()) + { + tsec.emplace_back(str.c_str(), str.c_str(), unsigned{}); + return; + } + const auto end = str.c_str() + str.size(); + + const wchar_t * word = nullptr; + for (auto i = str.c_str(); i != end; ++i) + { + wchar_t const ch = *i; + + //CKJ characters and whitespace + if (' ' == ch || '\t' == ch || (0x4E00 <= ch && ch <= 0x9FCF)) + { + if (word) //Record the word. + { + tsec.emplace_back(word, i, unsigned{}); + word = nullptr; + } + + tsec.emplace_back(i, i + 1, unsigned{}); + continue; } - if (str_w > pixels) //Indicates the splitting of ts string + if (nullptr == word) + word = i; + } + + if (word) + tsec.emplace_back(word, end, unsigned{}); + } + + row_coordinate _m_textline(std::size_t scrline) const + { + row_coordinate coord; + for (auto & mtr : linemtr_) + { + if (mtr.take_lines > scrline) { - std::size_t len = ts.end - ts.begin; -#ifdef _nana_std_has_string_view - auto pxbuf = editor_.graph_.glyph_pixels({ts.begin, len}); -#else - std::unique_ptr pxbuf(new unsigned[len]); - editor_.graph_.glyph_pixels(ts.begin, len, pxbuf.get()); -#endif + coord.second = scrline; + return coord; + } + else + scrline -= mtr.take_lines; - auto pxptr = pxbuf.get(); - auto pxend = pxptr + len; + ++coord.first; + } + return coord; + } + private: + text_editor& editor_; + std::vector linemtr_; + }; //end class behavior_linewrapped - secondary_begin = ts.begin; - text_px = 0; - for (auto pxi = pxptr; pxi != pxend; ++pxi) + class text_editor::keyword_parser + { + public: + void parse(const wchar_t* c_str, std::size_t len, implementation::inner_keywords& keywords) //need string_view + { + if (keywords.base.empty() || (0 == len) || (*c_str == 0)) + return; + + std::wstring text{ c_str, len }; + + using index = std::wstring::size_type; + + std::vector entities; + + ::nana::ciwstring cistr; + for (auto & ds : keywords.base) + { + index pos{ 0 }; + for (index rest{ text.size() }; rest >= ds.text.size(); ++pos, rest = text.size() - pos) + { + if (ds.case_sensitive) { - text_px += *pxi; - if (text_px < pixels) - continue; + pos = text.find(ds.text, pos); + if (pos == text.npos) + break; + } + else + { + if (cistr.empty()) + cistr.append(text.c_str(), text.size()); - const wchar_t * endptr = ts.begin + (pxi - pxptr) + (text_px == pixels ? 1 : 0); - line_sections.emplace_back(secondary_begin, endptr, unsigned{ text_px - (text_px == pixels ? 0 : *pxi) }); - secondary_begin = endptr; + pos = cistr.find(ds.text.c_str(), pos); + if (pos == cistr.npos) + break; + } - text_px = (text_px == pixels ? 0 : *pxi); + if (ds.whole_word_matched && (!_m_whole_word(text, pos, ds.text.size()))) + continue; + + auto ki = keywords.schemes.find(ds.scheme); + if ((ki != keywords.schemes.end()) && ki->second) + { +#ifdef _nana_std_has_emplace_return_type + auto & last = entities.emplace_back(); +#else + entities.emplace_back(); + auto & last = entities.back(); +#endif + last.begin = c_str + pos; + last.end = last.begin + ds.text.size(); + last.scheme = ki->second.get(); } } - continue; } - else if (text_px == pixels) + + if (!entities.empty()) { - line_sections.emplace_back(secondary_begin, ts.begin, unsigned{ text_px - str_w }); - secondary_begin = ts.begin; - text_px = str_w; - } - } - - auto & mtr = linemtr_[line]; - - mtr.take_lines = line_sections.size(); - mtr.line_sections.swap(line_sections); - - if (secondary_begin) - { - mtr.line_sections.emplace_back(secondary_begin, sections.back().end, unsigned{ text_px }); - ++mtr.take_lines; - } - } - - void pre_calc_lines(unsigned pixels) override - { - auto const lines = editor_.textbase().lines(); - linemtr_.resize(lines); - - for (std::size_t i = 0; i < lines; ++i) - pre_calc_line(i, pixels); - } - - std::size_t take_lines() const override - { - std::size_t lines = 0; - for (auto & mtr : linemtr_) - lines += mtr.take_lines; - - return lines; - } - - std::size_t take_lines(std::size_t pos) const override - { - return (pos < linemtr_.size() ? linemtr_[pos].take_lines : 0); - } - private: - /// Split a text into multiple sections, a section indicates an english word or a CKJ character - void _m_text_section(const std::wstring& str, std::vector& tsec) - { - if (str.empty()) - { - tsec.emplace_back(str.c_str(), str.c_str(), unsigned{}); - return; - } - const auto end = str.c_str() + str.size(); - - const wchar_t * word = nullptr; - for (auto i = str.c_str(); i != end; ++i) - { - wchar_t const ch = *i; - - //CKJ characters and whitespace - if (' ' == ch || '\t' == ch || (0x4E00 <= ch && ch <= 0x9FCF)) - { - if (word) //Record the word. + std::sort(entities.begin(), entities.end(), [](const entity& a, const entity& b) { - tsec.emplace_back(word, i, unsigned{}); - word = nullptr; + return (a.begin < b.begin); + }); + + auto i = entities.begin(); + auto bound = i->end; + + for (++i; i != entities.end(); ) + { + if (bound > i->begin) + i = entities.erase(i); // erase overlaping. Left only the first. + else + ++i; } - - tsec.emplace_back(i, i + 1, unsigned{}); - continue; } - if (nullptr == word) - word = i; + entities_.swap(entities); } - if(word) - tsec.emplace_back(word, end, unsigned{}); - } - - row_coordinate _m_textline(std::size_t scrline) const - { - row_coordinate coord; - for (auto & mtr : linemtr_) + const std::vector& entities() const { - if (mtr.take_lines > scrline) + return entities_; + } + private: + static bool _m_whole_word(const std::wstring& text, std::wstring::size_type pos, std::size_t len) + { + if (pos) { - coord.second = scrline; - return coord; + auto chr = text[pos - 1]; + if ((std::iswalpha(chr) && !std::iswspace(chr)) || chr == '_') + return false; } - else - scrline -= mtr.take_lines; - ++coord.first; - } - return coord; - } - private: - text_editor& editor_; - std::vector linemtr_; - }; //end class behavior_linewrapped - - class text_editor::keyword_parser - { - public: - void parse(const wchar_t* c_str, std::size_t len, implementation::inner_keywords& keywords) //need string_view - { - if ( keywords.base.empty() || (0 == len) || (*c_str == 0) ) - return; - - std::wstring text{ c_str, len }; - - using index = std::wstring::size_type; - - std::vector entities; - - ::nana::ciwstring cistr; - for (auto & ds : keywords.base) - { - index pos{0} ; - for (index rest{text.size()}; rest >= ds.text.size() ; ++pos, rest = text.size() - pos) - { - if (ds.case_sensitive) - { - pos = text.find(ds.text, pos); - if (pos == text.npos) - break; - } - else - { - if (cistr.empty()) - cistr.append(text.c_str(), text.size()); - - pos = cistr.find(ds.text.c_str(), pos); - if (pos == cistr.npos) - break; - } - - if (ds.whole_word_matched && (!_m_whole_word(text, pos, ds.text.size()))) - continue; - - auto ki = keywords.schemes.find(ds.scheme); - if ((ki != keywords.schemes.end()) && ki->second) - { -#ifdef _nana_std_has_emplace_return_type - auto & last = entities.emplace_back(); -#else - entities.emplace_back(); - auto & last = entities.back(); -#endif - last.begin = c_str + pos; - last.end = last.begin + ds.text.size(); - last.scheme = ki->second.get(); - } - } - } - - if (!entities.empty()) - { - std::sort(entities.begin(), entities.end(), [](const entity& a, const entity& b) + if (pos + len < text.size()) { - return (a.begin < b.begin); - }); - - auto i = entities.begin(); - auto bound = i->end; - - for (++i; i != entities.end(); ) - { - if (bound > i->begin) - i = entities.erase(i); // erase overlaping. Left only the first. - else - ++i; + auto chr = text[pos + len]; + if ((std::iswalpha(chr) && !std::iswspace(chr)) || chr == '_') + return false; } + + return true; } + private: + std::vector entities_; + }; - entities_.swap(entities); - } + //class text_editor - const std::vector& entities() const - { - return entities_; - } - private: - static bool _m_whole_word(const std::wstring& text, std::wstring::size_type pos, std::size_t len) - { - if (pos) - { - auto chr = text[pos - 1]; - if ((std::iswalpha(chr) && !std::iswspace(chr)) || chr == '_') - return false; - } - - if (pos + len < text.size()) - { - auto chr = text[pos + len]; - if ((std::iswalpha(chr) && !std::iswspace(chr)) || chr == '_') - return false; - } - - return true; - } - private: - std::vector entities_; - }; - - //class text_editor - - text_editor::text_editor(window wd, graph_reference graph, const text_editor_scheme* schm) - : impl_(new implementation), + text_editor::text_editor(window wd, graph_reference graph, const text_editor_scheme* schm) + : impl_(new implementation), window_(wd), graph_(graph), scheme_(schm) - { - impl_->capacities.behavior = new behavior_normal(*this); - - text_area_.area.dimension(graph.size()); - - impl_->cview.reset(new content_view{ wd }); - impl_->cview->disp_area(text_area_.area, {}, {}, {}); - impl_->cview->events().scrolled = [this] { - this->reset_caret(); - }; - - impl_->cview->events().hover_outside = [this](const point& pos) { - mouse_caret(pos, false); - if (selection::mode::mouse_selected == select_.mode_selection || selection::mode::method_selected == select_.mode_selection) - set_end_caret(false); - }; - - API::create_caret(wd, { 1, line_height() }); - API::bgcolor(wd, colors::white); - API::fgcolor(wd, colors::black); - } - - text_editor::~text_editor() - { - //For instance of unique_ptr pimpl idiom. - - delete impl_->capacities.behavior; - delete impl_; - } - - size text_editor::caret_size() const - { - return { 1, line_height() }; - } - - const point& text_editor::content_origin() const - { - return impl_->cview->origin(); - } - - void text_editor::set_highlight(const std::string& name, const ::nana::color& fgcolor, const ::nana::color& bgcolor) - { - if (fgcolor.invisible() && bgcolor.invisible()) { - impl_->keywords.schemes.erase(name); - return; + impl_->capacities.behavior = new behavior_normal(*this); + + text_area_.area.dimension(graph.size()); + + impl_->cview.reset(new content_view{ wd }); + impl_->cview->disp_area(text_area_.area, {}, {}, {}); + impl_->cview->events().scrolled = [this] { + this->reset_caret(); + }; + + impl_->cview->events().hover_outside = [this](const point& pos) { + mouse_caret(pos, false); + if (selection::mode::mouse_selected == select_.mode_selection || selection::mode::method_selected == select_.mode_selection) + set_end_caret(false); + }; + + API::create_caret(wd, { 1, line_height() }); + API::bgcolor(wd, colors::white); + API::fgcolor(wd, colors::black); } - auto sp = std::make_shared(); - sp->fgcolor = fgcolor; - sp->bgcolor = bgcolor; - impl_->keywords.schemes[name].swap(sp); - } - - void text_editor::erase_highlight(const std::string& name) - { - impl_->keywords.schemes.erase(name); - } - - void text_editor::set_keyword(const ::std::wstring& kw, const std::string& name, bool case_sensitive, bool whole_word_matched) - { - for(auto & ds : impl_->keywords.base) + text_editor::~text_editor() { - if (ds.text == kw) - { - ds.scheme = name; - ds.case_sensitive = case_sensitive; - ds.whole_word_matched = whole_word_matched; - return; - } - } - - impl_->keywords.base.emplace_back(kw, name, case_sensitive, whole_word_matched); - } - - void text_editor::erase_keyword(const ::std::wstring& kw) - { - for (auto i = impl_->keywords.base.begin(); i != impl_->keywords.base.end(); ++i) - { - if (kw == i->text) - { - impl_->keywords.base.erase(i); - return; - } - } - } - - colored_area_access_interface& text_editor::colored_area() - { - return impl_->colored_area; - } - - void text_editor::set_accept(std::function pred) - { - impl_->capacities.pred_acceptive = std::move(pred); - } - - void text_editor::set_accept(accepts acceptive) - { - impl_->capacities.acceptive = acceptive; - } - - bool text_editor::respond_char(const arg_keyboard& arg) //key is a character of ASCII code - { - if (!API::window_enabled(window_)) - return false; - - char_type key = arg.key; - switch (key) - { - case keyboard::end_of_text: - copy(); - return false; - case keyboard::select_all: - select(true); - return true; - } - - if (attributes_.editable && (!impl_->capacities.pred_acceptive || impl_->capacities.pred_acceptive(key))) - { - switch (key) - { - case '\b': - backspace(true, true); break; - case '\n': case '\r': - enter(true, true); break; - case keyboard::sync_idel: - paste(); break; - case keyboard::tab: - put(static_cast(keyboard::tab)); break; - case keyboard::cancel: - cut(); - break; - case keyboard::end_of_medium: - undo(true); - break; - case keyboard::substitute: - undo(false); - break; - default: - if (!_m_accepts(key)) - return false; - - if (key > 0x7F || (32 <= key && key <= 126)) - put(key); - } - reset_caret(); - impl_->try_refresh = sync_graph::refresh; - return true; - } - return false; - } - - bool text_editor::respond_key(const arg_keyboard& arg) - { - char_type key = arg.key; - switch (key) - { - case keyboard::os_arrow_left: - case keyboard::os_arrow_right: - case keyboard::os_arrow_up: - case keyboard::os_arrow_down: - case keyboard::os_home: - case keyboard::os_end: - case keyboard::os_pageup: - case keyboard::os_pagedown: - _m_handle_move_key(arg); - break; - case keyboard::del: - // send delete to set_accept function - if (this->attr().editable && (!impl_->capacities.pred_acceptive || impl_->capacities.pred_acceptive(key))) - del(); - break; - default: - return false; - } - impl_->try_refresh = sync_graph::refresh; - return true; - } - - void text_editor::typeface_changed() - { - _m_reset_content_size(true); - } - - void text_editor::indent(bool enb, std::function generator) - { - impl_->indent.enabled = enb; - impl_->indent.generator = std::move(generator); - } - - void text_editor::set_event(event_interface* ptr) - { - event_handler_ = ptr; - } - - bool text_editor::load(const char* fs) - { - if (!impl_->textbase.load(fs)) - return false; - - _m_reset(); - - impl_->try_refresh = sync_graph::refresh; - _m_reset_content_size(true); - return true; - } - - void text_editor::text_align(::nana::align alignment) - { - this->attributes_.alignment = alignment; - this->reset_caret(); - impl_->try_refresh = sync_graph::refresh; - _m_reset_content_size(); - } - - bool text_editor::text_area(const nana::rectangle& r) - { - if(text_area_.area == r) - return false; - - text_area_.area = r; - - if (impl_->counterpart.enabled) - impl_->counterpart.buffer.make(r.dimension()); - - impl_->cview->disp_area(r, { -1, 1 }, { 1, -1 }, { 2, 2 }); - if (impl_->cview->content_size().empty() || this->attributes_.line_wrapped) - _m_reset_content_size(true); - - reset_caret(); - return true; - } - - rectangle text_editor::text_area(bool including_scroll) const - { - return (including_scroll ? impl_->cview->view_area() : text_area_.area); - } - - bool text_editor::tip_string(::std::string&& str) - { - if(attributes_.tip_string == str) - return false; - - attributes_.tip_string = std::move(str); - return true; - } - - const text_editor::attributes& text_editor::attr() const noexcept - { - return attributes_; - } - - bool text_editor::line_wrapped(bool autl) - { - if (autl != attributes_.line_wrapped) - { - attributes_.line_wrapped = autl; + //For instance of unique_ptr pimpl idiom. delete impl_->capacities.behavior; - if (autl) - impl_->capacities.behavior = new behavior_linewrapped(*this); - else - impl_->capacities.behavior = new behavior_normal(*this); - - _m_reset_content_size(true); - - impl_->cview->move_origin(point{} -impl_->cview->origin()); - move_caret(upoint{}); - - impl_->try_refresh = sync_graph::refresh; - return true; - } - return false; - } - - bool text_editor::multi_lines(bool ml) - { - if((ml == false) && attributes_.multi_lines) - { - //retain the first line and remove the extra lines - if (impl_->textbase.erase(1, impl_->textbase.lines() - 1)) - _m_reset(); + delete impl_; } - if (attributes_.multi_lines == ml) - return false; - - attributes_.multi_lines = ml; - - if (!ml) - line_wrapped(false); - - _m_reset_content_size(); - impl_->cview->enable_scrolls(ml ? content_view::scrolls::both : content_view::scrolls::none); - impl_->cview->move_origin(point{} -impl_->cview->origin()); - - impl_->try_refresh = sync_graph::refresh; - return true; - } - - void text_editor::editable(bool enable, bool enable_caret) - { - attributes_.editable = enable; - attributes_.enable_caret = (enable || enable_caret); - } - - void text_editor::enable_background(bool enb) - { - attributes_.enable_background = enb; - } - - void text_editor::enable_background_counterpart(bool enb) - { - impl_->counterpart.enabled = enb; - if (enb) - impl_->counterpart.buffer.make(text_area_.area.dimension()); - else - impl_->counterpart.buffer.release(); - } - - void text_editor::undo_enabled(bool enb) - { - impl_->undo.enable(enb); - } - - bool text_editor::undo_enabled() const - { - return impl_->undo.enabled(); - } - - void text_editor::undo_max_steps(std::size_t maxs) - { - impl_->undo.max_steps(maxs); - } - - std::size_t text_editor::undo_max_steps() const - { - return impl_->undo.max_steps(); - } - - void text_editor::clear_undo() - { - auto size = impl_->undo.max_steps(); - impl_->undo.max_steps(0); - impl_->undo.max_steps(size); - } - - auto text_editor::customized_renderers() -> renderers& - { - return impl_->customized_renderers; - } - - unsigned text_editor::line_height() const - { - unsigned ascent, descent, internal_leading; - unsigned px = 0; - if (graph_.text_metrics(ascent, descent, internal_leading)) - px = ascent + descent; - - impl_->cview->step(px, false); - return px; - } - - unsigned text_editor::screen_lines(bool completed_line) const - { - auto const line_px = line_height(); - if (line_px) + size text_editor::caret_size() const { - auto h = impl_->cview->view_area().height; - if (graph_ && h) + return { 1, line_height() }; + } + + const point& text_editor::content_origin() const + { + return impl_->cview->origin(); + } + + void text_editor::set_highlight(const std::string& name, const ::nana::color& fgcolor, const ::nana::color& bgcolor) + { + if (fgcolor.invisible() && bgcolor.invisible()) { - if (completed_line) - return (h / line_px); - - return (h / line_px + (h % line_px ? 1 : 0)); - } - } - return 0; - } - - bool text_editor::focus_changed(const arg_focus& arg) - { - bool renderred = false; - - if (arg.getting && (select_.a == select_.b)) //Do not change the selected text - { - bool select_all = false; - switch (select_.behavior) - { - case text_focus_behavior::select: - select_all = true; - break; - case text_focus_behavior::select_if_click: - select_all = (arg_focus::reason::mouse_press == arg.focus_reason); - break; - case text_focus_behavior::select_if_tabstop: - select_all = (arg_focus::reason::tabstop == arg.focus_reason); - break; - case text_focus_behavior::select_if_tabstop_or_click: - select_all = (arg_focus::reason::tabstop == arg.focus_reason || arg_focus::reason::mouse_press == arg.focus_reason); - default: - break; - } - - if (select_all) - { - select(true); - move_caret_end(false); - renderred = true; - - //If the text widget is focused by clicking mouse button, the selected text will be cancelled - //by the subsequent mouse down event. In this situation, the subsequent mouse down event should - //be ignored. - select_.ignore_press = (arg_focus::reason::mouse_press == arg.focus_reason); - } - } - show_caret(arg.getting); - reset_caret(); - return renderred; - } - - bool text_editor::mouse_enter(bool entering) - { - if ((false == entering) && (false == text_area_.captured)) - API::window_cursor(window_, nana::cursor::arrow); - - return false; - } - - bool text_editor::mouse_move(bool left_button, const point& scrpos) - { - cursor cur = cursor::iterm; - if(((!hit_text_area(scrpos)) && (!text_area_.captured)) || !attributes_.enable_caret || !API::window_enabled(window_)) - cur = cursor::arrow; - - API::window_cursor(window_, cur); - - if(!attributes_.enable_caret) - return false; - - if(left_button) - { - mouse_caret(scrpos, false); - - if (selection::mode::mouse_selected == select_.mode_selection || selection::mode::method_selected == select_.mode_selection) - set_end_caret(false); - else if (selection::mode::move_selected == select_.mode_selection) - select_.mode_selection = selection::mode::move_selected_take_effect; - - impl_->try_refresh = sync_graph::refresh; - return true; - } - return false; - } - - void text_editor::mouse_pressed(const arg_mouse& arg) - { - if(!attributes_.enable_caret) - return; - - if (event_code::mouse_down == arg.evt_code) - { - if (select_.ignore_press || (!hit_text_area(arg.pos))) - { - select_.ignore_press = false; + impl_->keywords.schemes.erase(name); return; } - if (::nana::mouse::left_button == arg.button) - { - API::set_capture(window_, true); - text_area_.captured = true; + auto sp = std::make_shared(); + sp->fgcolor = fgcolor; + sp->bgcolor = bgcolor; + impl_->keywords.schemes[name].swap(sp); + } - if (this->hit_select_area(_m_coordinate_to_caret(arg.pos), true)) + void text_editor::erase_highlight(const std::string& name) + { + impl_->keywords.schemes.erase(name); + } + + void text_editor::set_keyword(const ::std::wstring& kw, const std::string& name, bool case_sensitive, bool whole_word_matched) + { + for (auto & ds : impl_->keywords.base) + { + if (ds.text == kw) { - //The selected of text can be moved only if it is editable - if (attributes_.editable) - select_.mode_selection = selection::mode::move_selected; + ds.scheme = name; + ds.case_sensitive = case_sensitive; + ds.whole_word_matched = whole_word_matched; + return; } - else + } + + impl_->keywords.base.emplace_back(kw, name, case_sensitive, whole_word_matched); + } + + void text_editor::erase_keyword(const ::std::wstring& kw) + { + for (auto i = impl_->keywords.base.begin(); i != impl_->keywords.base.end(); ++i) + { + if (kw == i->text) { - //Set caret pos by screen point and get the caret pos. - mouse_caret(arg.pos, true); - if (arg.shift) + impl_->keywords.base.erase(i); + return; + } + } + } + + colored_area_access_interface& text_editor::colored_area() + { + return impl_->colored_area; + } + + void text_editor::set_accept(std::function pred) + { + impl_->capacities.pred_acceptive = std::move(pred); + } + + void text_editor::set_accept(accepts acceptive) + { + impl_->capacities.acceptive = acceptive; + } + + bool text_editor::respond_char(const arg_keyboard& arg) //key is a character of ASCII code + { + if (!API::window_enabled(window_)) + return false; + + char_type key = arg.key; + switch (key) + { + case keyboard::end_of_text: + copy(); + return false; + case keyboard::select_all: + select(true); + return true; + } + + if (attributes_.editable && (!impl_->capacities.pred_acceptive || impl_->capacities.pred_acceptive(key))) + { + switch (key) + { + case '\b': + backspace(true, true); break; + case '\n': case '\r': + enter(true, true); break; + case keyboard::sync_idel: + paste(); break; + case keyboard::tab: + put(static_cast(keyboard::tab)); break; + case keyboard::cancel: + cut(); + break; + case keyboard::end_of_medium: + undo(true); + break; + case keyboard::substitute: + undo(false); + break; + default: + if (!_m_accepts(key)) + return false; + + if (key > 0x7F || (32 <= key && key <= 126)) + put(key); + } + reset_caret(); + impl_->try_refresh = sync_graph::refresh; + return true; + } + return false; + } + + bool text_editor::respond_key(const arg_keyboard& arg) + { + char_type key = arg.key; + switch (key) + { + case keyboard::os_arrow_left: + case keyboard::os_arrow_right: + case keyboard::os_arrow_up: + case keyboard::os_arrow_down: + case keyboard::os_home: + case keyboard::os_end: + case keyboard::os_pageup: + case keyboard::os_pagedown: + _m_handle_move_key(arg); + break; + case keyboard::del: + // send delete to set_accept function + if (this->attr().editable && (!impl_->capacities.pred_acceptive || impl_->capacities.pred_acceptive(key))) + del(); + break; + default: + return false; + } + impl_->try_refresh = sync_graph::refresh; + return true; + } + + void text_editor::typeface_changed() + { + _m_reset_content_size(true); + } + + void text_editor::indent(bool enb, std::function generator) + { + impl_->indent.enabled = enb; + impl_->indent.generator = std::move(generator); + } + + void text_editor::set_event(event_interface* ptr) + { + event_handler_ = ptr; + } + + bool text_editor::load(const char* fs) + { + if (!impl_->textbase.load(fs)) + return false; + + _m_reset(); + + impl_->try_refresh = sync_graph::refresh; + _m_reset_content_size(true); + return true; + } + + void text_editor::text_align(::nana::align alignment) + { + this->attributes_.alignment = alignment; + this->reset_caret(); + impl_->try_refresh = sync_graph::refresh; + _m_reset_content_size(); + } + + bool text_editor::text_area(const nana::rectangle& r) + { + if (text_area_.area == r) + return false; + + text_area_.area = r; + + if (impl_->counterpart.enabled) + impl_->counterpart.buffer.make(r.dimension()); + + impl_->cview->disp_area(r, { -1, 1 }, { 1, -1 }, { 2, 2 }); + if (impl_->cview->content_size().empty() || this->attributes_.line_wrapped) + _m_reset_content_size(true); + + reset_caret(); + return true; + } + + rectangle text_editor::text_area(bool including_scroll) const + { + return (including_scroll ? impl_->cview->view_area() : text_area_.area); + } + + bool text_editor::tip_string(::std::string&& str) + { + if (attributes_.tip_string == str) + return false; + + attributes_.tip_string = std::move(str); + return true; + } + + const text_editor::attributes& text_editor::attr() const noexcept + { + return attributes_; + } + + bool text_editor::line_wrapped(bool autl) + { + if (autl != attributes_.line_wrapped) + { + attributes_.line_wrapped = autl; + + delete impl_->capacities.behavior; + if (autl) + impl_->capacities.behavior = new behavior_linewrapped(*this); + else + impl_->capacities.behavior = new behavior_normal(*this); + + _m_reset_content_size(true); + + impl_->cview->move_origin(point{} -impl_->cview->origin()); + move_caret(upoint{}); + + impl_->try_refresh = sync_graph::refresh; + return true; + } + return false; + } + + bool text_editor::multi_lines(bool ml) + { + if ((ml == false) && attributes_.multi_lines) + { + //retain the first line and remove the extra lines + if (impl_->textbase.erase(1, impl_->textbase.lines() - 1)) + _m_reset(); + } + + if (attributes_.multi_lines == ml) + return false; + + attributes_.multi_lines = ml; + + if (!ml) + line_wrapped(false); + + _m_reset_content_size(); + impl_->cview->enable_scrolls(ml ? content_view::scrolls::both : content_view::scrolls::none); + impl_->cview->move_origin(point{} -impl_->cview->origin()); + + impl_->try_refresh = sync_graph::refresh; + return true; + } + + void text_editor::editable(bool enable, bool enable_caret) + { + attributes_.editable = enable; + attributes_.enable_caret = (enable || enable_caret); + } + + void text_editor::enable_background(bool enb) + { + attributes_.enable_background = enb; + } + + void text_editor::enable_background_counterpart(bool enb) + { + impl_->counterpart.enabled = enb; + if (enb) + impl_->counterpart.buffer.make(text_area_.area.dimension()); + else + impl_->counterpart.buffer.release(); + } + + void text_editor::undo_enabled(bool enb) + { + impl_->undo.enable(enb); + } + + bool text_editor::undo_enabled() const + { + return impl_->undo.enabled(); + } + + void text_editor::undo_max_steps(std::size_t maxs) + { + impl_->undo.max_steps(maxs); + } + + std::size_t text_editor::undo_max_steps() const + { + return impl_->undo.max_steps(); + } + + void text_editor::clear_undo() + { + auto size = impl_->undo.max_steps(); + impl_->undo.max_steps(0); + impl_->undo.max_steps(size); + } + + auto text_editor::customized_renderers() -> renderers& + { + return impl_->customized_renderers; + } + + unsigned text_editor::line_height() const + { + unsigned ascent, descent, internal_leading; + unsigned px = 0; + if (graph_.text_metrics(ascent, descent, internal_leading)) + px = ascent + descent; + + impl_->cview->step(px, false); + return px; + } + + unsigned text_editor::screen_lines(bool completed_line) const + { + auto const line_px = line_height(); + if (line_px) + { + auto h = impl_->cview->view_area().height; + if (graph_ && h) + { + if (completed_line) + return (h / line_px); + + return (h / line_px + (h % line_px ? 1 : 0)); + } + } + return 0; + } + + bool text_editor::focus_changed(const arg_focus& arg) + { + bool renderred = false; + + if (arg.getting && (select_.a == select_.b)) //Do not change the selected text + { + bool select_all = false; + switch (select_.behavior) + { + case text_focus_behavior::select: + select_all = true; + break; + case text_focus_behavior::select_if_click: + select_all = (arg_focus::reason::mouse_press == arg.focus_reason); + break; + case text_focus_behavior::select_if_tabstop: + select_all = (arg_focus::reason::tabstop == arg.focus_reason); + break; + case text_focus_behavior::select_if_tabstop_or_click: + select_all = (arg_focus::reason::tabstop == arg.focus_reason || arg_focus::reason::mouse_press == arg.focus_reason); + default: + break; + } + + if (select_all) + { + select(true); + move_caret_end(false); + renderred = true; + + //If the text widget is focused by clicking mouse button, the selected text will be cancelled + //by the subsequent mouse down event. In this situation, the subsequent mouse down event should + //be ignored. + select_.ignore_press = (arg_focus::reason::mouse_press == arg.focus_reason); + } + } + show_caret(arg.getting); + reset_caret(); + return renderred; + } + + bool text_editor::mouse_enter(bool entering) + { + if ((false == entering) && (false == text_area_.captured)) + API::window_cursor(window_, nana::cursor::arrow); + + return false; + } + + bool text_editor::mouse_move(bool left_button, const point& scrpos) + { + cursor cur = cursor::iterm; + if (((!hit_text_area(scrpos)) && (!text_area_.captured)) || !attributes_.enable_caret || !API::window_enabled(window_)) + cur = cursor::arrow; + + API::window_cursor(window_, cur); + + if (!attributes_.enable_caret) + return false; + + if (left_button) + { + mouse_caret(scrpos, false); + + if (selection::mode::mouse_selected == select_.mode_selection || selection::mode::method_selected == select_.mode_selection) + set_end_caret(false); + else if (selection::mode::move_selected == select_.mode_selection) + select_.mode_selection = selection::mode::move_selected_take_effect; + + impl_->try_refresh = sync_graph::refresh; + return true; + } + return false; + } + + void text_editor::mouse_pressed(const arg_mouse& arg) + { + if (!attributes_.enable_caret) + return; + + if (event_code::mouse_down == arg.evt_code) + { + if (select_.ignore_press || (!hit_text_area(arg.pos))) + { + select_.ignore_press = false; + return; + } + + if (::nana::mouse::left_button == arg.button) + { + API::set_capture(window_, true); + text_area_.captured = true; + + if (this->hit_select_area(_m_coordinate_to_caret(arg.pos), true)) { - if (points_.shift_begin_caret != points_.caret) - { - select_.a = points_.shift_begin_caret; - select_.b = points_.caret; - } + //The selected of text can be moved only if it is editable + if (attributes_.editable) + select_.mode_selection = selection::mode::move_selected; } else { - if (!select(false)) + //Set caret pos by screen point and get the caret pos. + mouse_caret(arg.pos, true); + if (arg.shift) { - select_.a = points_.caret; //Set begin caret - set_end_caret(true); + if (points_.shift_begin_caret != points_.caret) + { + select_.a = points_.shift_begin_caret; + select_.b = points_.caret; + } } - points_.shift_begin_caret = points_.caret; + else + { + if (!select(false)) + { + select_.a = points_.caret; //Set begin caret + set_end_caret(true); + } + points_.shift_begin_caret = points_.caret; + } + select_.mode_selection = selection::mode::mouse_selected; } - select_.mode_selection = selection::mode::mouse_selected; } + + impl_->try_refresh = sync_graph::refresh; } - - impl_->try_refresh = sync_graph::refresh; - } - else if (event_code::mouse_up == arg.evt_code) - { - select_.ignore_press = false; - - if (select_.mode_selection == selection::mode::mouse_selected) + else if (event_code::mouse_up == arg.evt_code) { - select_.mode_selection = selection::mode::no_selected; - set_end_caret(true); - } - else if (selection::mode::move_selected == select_.mode_selection || selection::mode::move_selected_take_effect == select_.mode_selection) - { - //move_selected indicates the mouse is pressed on the selected text, but the mouse has not moved. So the text_editor should cancel the selection. - //move_selected_take_effect indicates the text_editor should try to move the selection. + select_.ignore_press = false; - if ((selection::mode::move_selected == select_.mode_selection) || !move_select()) + if (select_.mode_selection == selection::mode::mouse_selected) { - //no move occurs - select(false); - move_caret(_m_coordinate_to_caret(arg.pos)); + select_.mode_selection = selection::mode::no_selected; + set_end_caret(true); + } + else if (selection::mode::move_selected == select_.mode_selection || selection::mode::move_selected_take_effect == select_.mode_selection) + { + //move_selected indicates the mouse is pressed on the selected text, but the mouse has not moved. So the text_editor should cancel the selection. + //move_selected_take_effect indicates the text_editor should try to move the selection. + + if ((selection::mode::move_selected == select_.mode_selection) || !move_select()) + { + //no move occurs + select(false); + move_caret(_m_coordinate_to_caret(arg.pos)); + } + + select_.mode_selection = selection::mode::no_selected; + impl_->try_refresh = sync_graph::refresh; } - select_.mode_selection = selection::mode::no_selected; - impl_->try_refresh = sync_graph::refresh; - } + API::release_capture(window_); - API::release_capture(window_); - - text_area_.captured = false; - if (hit_text_area(arg.pos) == false) - API::window_cursor(window_, nana::cursor::arrow); - } - } - - //Added Windows-style mouse double-click to the textbox(https://github.com/cnjinhao/nana/pull/229) - //Oleg Smolsky - bool text_editor::select_word(const arg_mouse& arg) - { - if(!attributes_.enable_caret) - return false; - - // Set caret pos by screen point and get the caret pos. - mouse_caret(arg.pos, true); - - // Set the initial selection: it is an empty range. - select_.a = select_.b = points_.caret; - const auto& line = impl_->textbase.getline(select_.b.y); - - // Expand the selection forward to the word's end. - while (select_.b.x < line.size() && !std::iswspace(line[select_.b.x])) - ++select_.b.x; - - // Expand the selection backward to the word's start. - while (select_.a.x > 0 && !std::iswspace(line[select_.a.x - 1])) - --select_.a.x; - - select_.mode_selection = selection::mode::method_selected; - impl_->try_refresh = sync_graph::refresh; - return true; - } - - textbase & text_editor::textbase() - { - return impl_->textbase; - } - - const textbase & text_editor::textbase() const - { - return impl_->textbase; - } - - bool text_editor::try_refresh() - { - if (sync_graph::none != impl_->try_refresh) - { - if (sync_graph::refresh == impl_->try_refresh) - render(API::is_focus_ready(window_)); - - impl_->try_refresh = sync_graph::none; - return true; - } - return false; - } - - bool text_editor::getline(std::size_t pos, std::wstring& text) const - { - if (impl_->textbase.lines() <= pos) - return false; - - text = impl_->textbase.getline(pos); - return true; - } - - void text_editor::text(std::wstring str, bool end_caret) - { - impl_->undo.clear(); - - impl_->textbase.erase_all(); - _m_reset(); - _m_reset_content_size(true); - - if (!end_caret) - { - auto undo_ptr = std::unique_ptr{ new undo_input_text(*this, str) }; - undo_ptr->set_caret_pos(); - - _m_put(std::move(str), false); - - impl_->undo.push(std::move(undo_ptr)); - - if (graph_) - { - this->_m_adjust_view(); - - reset_caret(); - impl_->try_refresh = sync_graph::refresh; - - //_m_put calcs the lines - _m_reset_content_size(true); - impl_->cview->sync(false); + text_area_.captured = false; + if (hit_text_area(arg.pos) == false) + API::window_cursor(window_, nana::cursor::arrow); } } - else - put(std::move(str), false); - textbase().text_changed(); - } - - std::wstring text_editor::text() const - { - std::wstring str; - std::size_t lines = impl_->textbase.lines(); - if(lines > 0) + //Added Windows-style mouse double-click to the textbox(https://github.com/cnjinhao/nana/pull/229) + //Oleg Smolsky + bool text_editor::select_word(const arg_mouse& arg) { - str = impl_->textbase.getline(0); - for(std::size_t i = 1; i < lines; ++i) - { - str += L"\n\r"; - str += impl_->textbase.getline(i); + if (!attributes_.enable_caret) + return false; + + // Set caret pos by screen point and get the caret pos. + mouse_caret(arg.pos, true); + + // Set the initial selection: it is an empty range. + select_.a = select_.b = points_.caret; + const auto& line = impl_->textbase.getline(select_.b.y); + + + + if (select_.a.x < line.size() && !std::isalnum(line[select_.a.x]) && line[select_.a.x] != '_') { + ++select_.b.x; } - } - return str; - } + else { + // Expand the selection forward to the word's end. + while (select_.b.x < line.size() && !std::iswspace(line[select_.b.x]) && (std::isalnum(line[select_.b.x]) || line[select_.b.x] == '_')) + ++select_.b.x; - bool text_editor::move_caret(upoint crtpos, bool stay_in_view) - { - const unsigned line_pixels = line_height(); - - if (crtpos != points_.caret) - { - //Check and make the crtpos available - if (crtpos.y < impl_->textbase.lines()) - { - crtpos.x = (std::min)(static_cast(impl_->textbase.getline(crtpos.y).size()), crtpos.x); + // Expand the selection backward to the word's start. + while (select_.a.x > 0 && !std::iswspace(line[select_.a.x - 1]) && (std::isalnum(line[select_.a.x - 1]) || line[select_.a.x - 1] == '_')) + --select_.a.x; } - else - { - crtpos.y = static_cast(impl_->textbase.lines()); - if (crtpos.y > 0) - --crtpos.y; - - crtpos.x = static_cast(impl_->textbase.getline(crtpos.y).size()); - } - - points_.caret = crtpos; - } - - //The coordinate of caret - auto coord = _m_caret_to_coordinate(crtpos); - - const int line_bottom = coord.y + static_cast(line_pixels); - - if (!API::is_focus_ready(window_)) - return false; - - auto caret = API::open_caret(window_, true); - - bool visible = false; - auto text_area = impl_->cview->view_area(); - - if (text_area.is_hit(coord) && (line_bottom > text_area.y)) - { - visible = true; - if (line_bottom > text_area.bottom()) - caret->dimension(nana::size(1, line_pixels - (line_bottom - text_area.bottom()))); - else if (caret->dimension().height != line_pixels) - reset_caret_pixels(); - } - - if(!attributes_.enable_caret) - visible = false; - - caret->visible(visible); - if(visible) - caret->position(coord); - - //Adjust the caret into screen when the caret position is modified by this function - if (stay_in_view && (!hit_text_area(coord))) - { - if (_m_adjust_view()) - impl_->cview->sync(false); - - impl_->try_refresh = sync_graph::refresh; - caret->visible(true); - return true; - } - return false; - } - - void text_editor::move_caret_end(bool update) - { - points_.caret.y = static_cast(impl_->textbase.lines()); - if(points_.caret.y) --points_.caret.y; - points_.caret.x = static_cast(impl_->textbase.getline(points_.caret.y).size()); - - if (update) - this->reset_caret(); - } - - void text_editor::reset_caret_pixels() const - { - API::open_caret(window_, true).get()->dimension({ 1, line_height() }); - } - - void text_editor::reset_caret(bool stay_in_view) - { - move_caret(points_.caret, stay_in_view); - } - - void text_editor::show_caret(bool isshow) - { - if(isshow == false || API::is_focus_ready(window_)) - API::open_caret(window_, true).get()->visible(isshow); - } - - bool text_editor::selected() const - { - return (select_.a != select_.b); - } - - bool text_editor::get_selected_points(nana::upoint &a, nana::upoint &b) const - { - if (select_.a == select_.b) - return false; - - if (select_.a < select_.b) - { - a = select_.a; - b = select_.b; - } - else - { - a = select_.b; - b = select_.a; - } - - return true; - } - - bool text_editor::select(bool yes) - { - if (yes) - { - select_.a.x = select_.a.y = 0; - select_.b.y = static_cast(impl_->textbase.lines()); - if (select_.b.y) --select_.b.y; - select_.b.x = static_cast(impl_->textbase.getline(select_.b.y).size()); select_.mode_selection = selection::mode::method_selected; impl_->try_refresh = sync_graph::refresh; return true; } - select_.mode_selection = selection::mode::no_selected; - if (_m_cancel_select(0)) + textbase & text_editor::textbase() { + return impl_->textbase; + } + + const textbase & text_editor::textbase() const + { + return impl_->textbase; + } + + bool text_editor::try_refresh() + { + if (sync_graph::none != impl_->try_refresh) + { + if (sync_graph::refresh == impl_->try_refresh) + render(API::is_focus_ready(window_)); + + impl_->try_refresh = sync_graph::none; + return true; + } + return false; + } + + bool text_editor::getline(std::size_t pos, std::wstring& text) const + { + if (impl_->textbase.lines() <= pos) + return false; + + text = impl_->textbase.getline(pos); + return true; + } + + void text_editor::text(std::wstring str, bool end_caret) + { + impl_->undo.clear(); + + impl_->textbase.erase_all(); + _m_reset(); + _m_reset_content_size(true); + + if (!end_caret) + { + auto undo_ptr = std::unique_ptr{ new undo_input_text(*this, str) }; + undo_ptr->set_caret_pos(); + + _m_put(std::move(str), false); + + impl_->undo.push(std::move(undo_ptr)); + + if (graph_) + { + this->_m_adjust_view(); + + reset_caret(); + impl_->try_refresh = sync_graph::refresh; + + //_m_put calcs the lines + _m_reset_content_size(true); + impl_->cview->sync(false); + } + } + else + put(std::move(str), false); + + textbase().text_changed(); + } + + std::wstring text_editor::text() const + { + std::wstring str; + std::size_t lines = impl_->textbase.lines(); + if (lines > 0) + { + str = impl_->textbase.getline(0); + for (std::size_t i = 1; i < lines; ++i) + { + str += L"\n\r"; + str += impl_->textbase.getline(i); + } + } + return str; + } + + bool text_editor::move_caret(upoint crtpos, bool stay_in_view) + { + const unsigned line_pixels = line_height(); + + if (crtpos != points_.caret) + { + //Check and make the crtpos available + if (crtpos.y < impl_->textbase.lines()) + { + crtpos.x = (std::min)(static_cast(impl_->textbase.getline(crtpos.y).size()), crtpos.x); + } + else + { + crtpos.y = static_cast(impl_->textbase.lines()); + if (crtpos.y > 0) + --crtpos.y; + + crtpos.x = static_cast(impl_->textbase.getline(crtpos.y).size()); + } + + points_.caret = crtpos; + } + + //The coordinate of caret + auto coord = _m_caret_to_coordinate(crtpos); + + const int line_bottom = coord.y + static_cast(line_pixels); + + if (!API::is_focus_ready(window_)) + return false; + + auto caret = API::open_caret(window_, true); + + bool visible = false; + auto text_area = impl_->cview->view_area(); + + if (text_area.is_hit(coord) && (line_bottom > text_area.y)) + { + visible = true; + if (line_bottom > text_area.bottom()) + caret->dimension(nana::size(1, line_pixels - (line_bottom - text_area.bottom()))); + else if (caret->dimension().height != line_pixels) + reset_caret_pixels(); + } + + if (!attributes_.enable_caret) + visible = false; + + caret->visible(visible); + if (visible) + caret->position(coord); + + //Adjust the caret into screen when the caret position is modified by this function + if (stay_in_view && (!hit_text_area(coord))) + { + if (_m_adjust_view()) + impl_->cview->sync(false); + + impl_->try_refresh = sync_graph::refresh; + caret->visible(true); + return true; + } + return false; + } + + void text_editor::move_caret_end(bool update) + { + points_.caret.y = static_cast(impl_->textbase.lines()); + if (points_.caret.y) --points_.caret.y; + points_.caret.x = static_cast(impl_->textbase.getline(points_.caret.y).size()); + + if (update) + this->reset_caret(); + } + + void text_editor::reset_caret_pixels() const + { + API::open_caret(window_, true).get()->dimension({ 1, line_height() }); + } + + void text_editor::reset_caret(bool stay_in_view) + { + move_caret(points_.caret, stay_in_view); + } + + void text_editor::show_caret(bool isshow) + { + if (isshow == false || API::is_focus_ready(window_)) + API::open_caret(window_, true).get()->visible(isshow); + } + + bool text_editor::selected() const + { + return (select_.a != select_.b); + } + + bool text_editor::get_selected_points(nana::upoint &a, nana::upoint &b) const + { + if (select_.a == select_.b) + return false; + + if (select_.a < select_.b) + { + a = select_.a; + b = select_.b; + } + else + { + a = select_.b; + b = select_.a; + } + + return true; + } + + bool text_editor::select(bool yes) + { + if (yes) + { + select_.a.x = select_.a.y = 0; + select_.b.y = static_cast(impl_->textbase.lines()); + if (select_.b.y) --select_.b.y; + select_.b.x = static_cast(impl_->textbase.getline(select_.b.y).size()); + select_.mode_selection = selection::mode::method_selected; + impl_->try_refresh = sync_graph::refresh; + return true; + } + + select_.mode_selection = selection::mode::no_selected; + if (_m_cancel_select(0)) + { + impl_->try_refresh = sync_graph::refresh; + return true; + } + return false; + } + + bool text_editor::select_points(nana::upoint arg_a, nana::upoint arg_b) + { + select_.a = arg_a; + select_.b = arg_b; + select_.mode_selection = selection::mode::method_selected; impl_->try_refresh = sync_graph::refresh; return true; } - return false; - } - bool text_editor::select_points(nana::upoint arg_a, nana::upoint arg_b) - { - select_.a = arg_a; - select_.b = arg_b; - select_.mode_selection = selection::mode::method_selected; - impl_->try_refresh = sync_graph::refresh; - return true; - } - - void text_editor::set_end_caret(bool stay_in_view) - { - bool new_sel_end = (select_.b != points_.caret); - select_.b = points_.caret; - - if (new_sel_end || (stay_in_view && this->_m_adjust_view())) - impl_->try_refresh = sync_graph::refresh; - } - - bool text_editor::hit_text_area(const point& pos) const - { - return impl_->cview->view_area().is_hit(pos); - } - - bool text_editor::hit_select_area(nana::upoint pos, bool ignore_when_select_all) const - { - nana::upoint a, b; - if (get_selected_points(a, b)) + void text_editor::set_end_caret(bool stay_in_view) { - if (ignore_when_select_all) + bool new_sel_end = (select_.b != points_.caret); + select_.b = points_.caret; + + if (new_sel_end || (stay_in_view && this->_m_adjust_view())) + impl_->try_refresh = sync_graph::refresh; + } + + bool text_editor::hit_text_area(const point& pos) const + { + return impl_->cview->view_area().is_hit(pos); + } + + bool text_editor::hit_select_area(nana::upoint pos, bool ignore_when_select_all) const + { + nana::upoint a, b; + if (get_selected_points(a, b)) { - if (a.x == 0 && a.y == 0 && (b.y + 1) == static_cast(textbase().lines())) + if (ignore_when_select_all) { - //is select all - if (b.x == static_cast(textbase().getline(b.y).size())) - return false; + if (a.x == 0 && a.y == 0 && (b.y + 1) == static_cast(textbase().lines())) + { + //is select all + if (b.x == static_cast(textbase().getline(b.y).size())) + return false; + } + } + + if ((pos.y > a.y || (pos.y == a.y && pos.x >= a.x)) && ((pos.y < b.y) || (pos.y == b.y && pos.x < b.x))) + return true; + } + return false; + } + + bool text_editor::move_select() + { + if (hit_select_area(points_.caret, true) || (select_.b == points_.caret)) + { + points_.caret = select_.b; + + if (this->_m_adjust_view()) + impl_->try_refresh = sync_graph::refresh; + + reset_caret(); + return true; + } + + if (_m_move_select(true)) + { + textbase().text_changed(); + this->_m_adjust_view(); + impl_->try_refresh = sync_graph::refresh; + return true; + } + return false; + } + + bool text_editor::mask(wchar_t ch) + { + if (mask_char_ == ch) + return false; + + mask_char_ = ch; + return true; + } + + unsigned text_editor::width_pixels() const + { + unsigned exclude_px = API::open_caret(window_, true).get()->dimension().width; + + if (attributes_.line_wrapped) + exclude_px += impl_->cview->extra_space(false); + + return (text_area_.area.width > exclude_px ? text_area_.area.width - exclude_px : 0); + } + + window text_editor::window_handle() const + { + return window_; + } + + const std::vector& text_editor::text_position() const + { + return impl_->text_position; + } + + void text_editor::focus_behavior(text_focus_behavior behavior) + { + select_.behavior = behavior; + } + + void text_editor::select_behavior(bool move_to_end) + { + select_.move_to_end = move_to_end; + } + + std::size_t text_editor::line_count(bool text_lines) const + { + if (text_lines) + return textbase().lines(); + + 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_); + } + + void text_editor::render(bool has_focus) + { + const auto bgcolor = _m_bgcolor(); + + auto fgcolor = scheme_->foreground.get_color(); + if (!API::window_enabled(window_)) + fgcolor.blend(bgcolor, 0.5); + + if (API::widget_borderless(window_)) + graph_.rectangle(false, bgcolor); + + //Draw background + if (!API::dev::copy_transparent_background(window_, graph_)) + { + if (attributes_.enable_background) + graph_.rectangle(text_area_.area, true, bgcolor); + } + + if (impl_->customized_renderers.background) + impl_->customized_renderers.background(graph_, text_area_.area, bgcolor); + + if (impl_->counterpart.buffer && !text_area_.area.empty()) + impl_->counterpart.buffer.bitblt(rectangle{ text_area_.area.dimension() }, graph_, text_area_.area.position()); + + //Render the content when the text isn't empty or the window has got focus, + //otherwise draw the tip string. + if ((false == textbase().empty()) || has_focus) + { + auto text_pos = _m_render_text(fgcolor); + + if (text_pos.empty()) + text_pos.emplace_back(upoint{}); + + if ((impl_->text_position_origin != impl_->cview->origin().y) || (text_pos != impl_->text_position)) + { + impl_->text_position_origin = impl_->cview->origin().y; + impl_->text_position.swap(text_pos); + if (event_handler_) + event_handler_->text_exposed(impl_->text_position); + } + } + else //Draw tip string + { + graph_.string({ text_area_.area.x - impl_->cview->origin().x, text_area_.area.y }, attributes_.tip_string, static_cast(0x787878)); + } + + if (impl_->text_position.empty()) + impl_->text_position.emplace_back(upoint{}); + + _m_draw_border(); + impl_->try_refresh = sync_graph::none; + } + //public: + void text_editor::put(std::wstring text, bool perform_event) + { + if (text.empty()) + return; + + auto undo_ptr = std::unique_ptr{ new undo_input_text(*this, text) }; + + undo_ptr->set_selected_text(); + + //Do not forget to assign the _m_erase_select() to caret + //because _m_put() will insert the text at the position where the caret is. + points_.caret = _m_erase_select(false); + + undo_ptr->set_caret_pos(); + points_.caret = _m_put(std::move(text), false); + + impl_->undo.push(std::move(undo_ptr)); + + _m_reset_content_size(true); + if (perform_event) + textbase().text_changed(); + + if (graph_) + { + if (this->_m_adjust_view()) + impl_->cview->sync(false); + + reset_caret(); + impl_->try_refresh = sync_graph::refresh; + } + } + + void text_editor::put(wchar_t ch) + { + std::wstring ch_str(1, ch); + + auto undo_ptr = std::unique_ptr{ new undo_input_text(*this, ch_str) }; + bool refresh = (select_.a != select_.b); + + undo_ptr->set_selected_text(); + if (refresh) + points_.caret = _m_erase_select(false); + + undo_ptr->set_caret_pos(); + + impl_->undo.push(std::move(undo_ptr)); + + auto secondary_before = impl_->capacities.behavior->take_lines(points_.caret.y); + textbase().insert(points_.caret, std::move(ch_str)); + _m_pre_calc_lines(points_.caret.y, 1); + + textbase().text_changed(); + + points_.caret.x++; + + _m_reset_content_size(); + + if (!refresh) + { + if (!_m_update_caret_line(secondary_before)) + draw_corner(); + } + else + impl_->try_refresh = sync_graph::refresh; + + } + + void text_editor::copy() const + { + //Disallows copying text if the text_editor is masked. + if (mask_char_) + return; + + auto text = _m_make_select_string(); + if (!text.empty()) + nana::system::dataexch().set(text, API::root(this->window_)); + } + + void text_editor::cut() + { + copy(); + del(); + } + + void text_editor::paste() + { + auto text = system::dataexch{}.wget(); + + if ((accepts::no_restrict == impl_->capacities.acceptive) || !impl_->capacities.pred_acceptive) + { + put(move(text), true); + return; + } + + //Check if the input is acceptable + for (auto i = text.begin(); i != text.end(); ++i) + { + if (_m_accepts(*i)) + { + if (accepts::no_restrict == impl_->capacities.acceptive) + put(*i); + + continue; + } + + if (accepts::no_restrict != impl_->capacities.acceptive) + { + text.erase(i, text.end()); + put(move(text), true); + } + break; + } + } + + void text_editor::enter(bool record_undo, bool perform_event) + { + if (false == attributes_.multi_lines) + return; + + auto undo_ptr = std::unique_ptr(new undo_input_text(*this, std::wstring(1, '\n'))); + + undo_ptr->set_selected_text(); + points_.caret = _m_erase_select(false); + + undo_ptr->set_caret_pos(); + + auto & textbase = this->textbase(); + + const string_type& lnstr = textbase.getline(points_.caret.y); + ++points_.caret.y; + + if (lnstr.size() > points_.caret.x) + { + //Breaks the line and moves the rest part to a new line + auto rest_part_len = lnstr.size() - points_.caret.x; //Firstly get the length of rest part, because lnstr may be invalid after insertln + textbase.insertln(points_.caret.y, lnstr.substr(points_.caret.x)); + textbase.erase(points_.caret.y - 1, points_.caret.x, rest_part_len); + } + else + { + if (textbase.lines() == 0) + textbase.insertln(0, std::wstring{}); + textbase.insertln(points_.caret.y, std::wstring{}); + } + + if (record_undo) + impl_->undo.push(std::move(undo_ptr)); + + impl_->capacities.behavior->add_lines(points_.caret.y - 1, 1); + _m_pre_calc_lines(points_.caret.y - 1, 2); + + points_.caret.x = 0; + + auto origin = impl_->cview->origin(); + origin.x = 0; + + if (impl_->indent.enabled) + { + if (impl_->indent.generator) + { + put(nana::to_wstring(impl_->indent.generator()), false); + } + else + { + auto & text = textbase.getline(points_.caret.y - 1); + auto indent_pos = text.find_first_not_of(L"\t "); + if (indent_pos != std::wstring::npos) + put(text.substr(0, indent_pos), false); + else + put(text, false); + } + } + else + _m_reset_content_size(); + + if (perform_event) + textbase.text_changed(); + + auto origin_moved = impl_->cview->move_origin(origin - impl_->cview->origin()); + + if (this->_m_adjust_view() || origin_moved) + impl_->cview->sync(true); + } + + void text_editor::del() + { + if (select_.a == select_.b) + { + if (textbase().getline(points_.caret.y).size() > points_.caret.x) + { + ++points_.caret.x; + } + else if (points_.caret.y + 1 < textbase().lines()) + { //Move to next line + points_.caret.x = 0; + ++points_.caret.y; + } + else + return; //No characters behind the caret + } + + backspace(true, true); + } + + void text_editor::backspace(bool record_undo, bool perform_event) + { + auto undo_ptr = std::unique_ptr(new undo_backspace(*this)); + bool has_to_redraw = true; + if (select_.a == select_.b) + { + auto & textbase = this->textbase(); + if (points_.caret.x) + { + unsigned erase_number = 1; + --points_.caret.x; + + auto& lnstr = textbase.getline(points_.caret.y); + + undo_ptr->set_caret_pos(); + undo_ptr->set_removed(lnstr.substr(points_.caret.x, erase_number)); + auto secondary = impl_->capacities.behavior->take_lines(points_.caret.y); + textbase.erase(points_.caret.y, points_.caret.x, erase_number); + _m_pre_calc_lines(points_.caret.y, 1); + + if (!this->_m_adjust_view()) + { + _m_update_line(points_.caret.y, secondary); + has_to_redraw = false; + } + } + else if (points_.caret.y) + { + points_.caret.x = static_cast(textbase.getline(--points_.caret.y).size()); + textbase.merge(points_.caret.y); + impl_->capacities.behavior->merge_lines(points_.caret.y, points_.caret.y + 1); + undo_ptr->set_caret_pos(); + undo_ptr->set_removed(std::wstring(1, '\n')); + } + else + undo_ptr.reset(); + } + else + { + undo_ptr->set_selected_text(); + points_.caret = _m_erase_select(false); + undo_ptr->set_caret_pos(); + } + + if (record_undo) + impl_->undo.push(std::move(undo_ptr)); + + _m_reset_content_size(false); + + if (perform_event) + textbase().text_changed(); + + textbase().text_changed(); + + if (has_to_redraw) + { + this->_m_adjust_view(); + impl_->try_refresh = sync_graph::refresh; + } + } + + void text_editor::undo(bool reverse) + { + if (reverse) + impl_->undo.redo(); + else + impl_->undo.undo(); + + _m_reset_content_size(true); + + this->_m_adjust_view(); + impl_->try_refresh = sync_graph::refresh; + } + + void text_editor::set_undo_queue_length(std::size_t len) + { + impl_->undo.max_steps(len); + } + + void text_editor::move_ns(bool to_north) + { + const bool redraw_required = _m_cancel_select(0); + if (_m_move_caret_ns(to_north) || redraw_required) + impl_->try_refresh = sync_graph::refresh; + } + + void text_editor::move_left() + { + bool pending = true; + if (_m_cancel_select(1) == false) + { + if (points_.caret.x) + { + --points_.caret.x; + pending = false; + if (this->_m_adjust_view()) + impl_->try_refresh = sync_graph::refresh; + } + else if (points_.caret.y) //Move to previous line + points_.caret.x = static_cast(textbase().getline(--points_.caret.y).size()); + else + pending = false; + } + + if (pending && this->_m_adjust_view()) + impl_->try_refresh = sync_graph::refresh; + } + + void text_editor::move_right() + { + bool do_render = false; + if (_m_cancel_select(2) == false) + { + auto lnstr = textbase().getline(points_.caret.y); + if (lnstr.size() > points_.caret.x) + { + ++points_.caret.x; + do_render = this->_m_adjust_view(); + } + else if (points_.caret.y + 1 < textbase().lines()) + { //Move to next line + points_.caret.x = 0; + ++points_.caret.y; + do_render = this->_m_adjust_view(); + } + } + else + do_render = this->_m_adjust_view(); + + if (do_render) + impl_->try_refresh = sync_graph::refresh; + } + + void text_editor::_m_handle_move_key(const arg_keyboard& arg) + { + if (arg.shift && (select_.a == select_.b)) + select_.a = select_.b = points_.caret; + + auto origin = impl_->cview->origin(); + auto pos = points_.caret; + auto coord = _m_caret_to_coordinate(points_.caret, false); + + wchar_t key = arg.key; + + auto const line_px = this->line_height(); + + //The number of text lines + auto const line_count = textbase().lines(); + + //The number of charecters in the line of caret + auto const text_length = textbase().getline(points_.caret.y).size(); + + + switch (key) { + case keyboard::os_arrow_left: + if (select_.move_to_end && (select_.a != select_.b) && (!arg.shift)) + { + pos = select_.a; + } + else if (pos.x != 0) + { + --pos.x; + } + else if (pos.y != 0) { + --pos.y; + pos.x = static_cast(textbase().getline(pos.y).size()); + } + break; + case keyboard::os_arrow_right: + if (select_.move_to_end && (select_.a != select_.b) && (!arg.shift)) + { + pos = select_.b; + } + else if (pos.x < text_length) + { + ++pos.x; + } + else if (pos.y != line_count - 1) + { + ++pos.y; + pos.x = 0; + } + break; + case keyboard::os_arrow_up: + coord.y -= static_cast(line_px); + break; + case keyboard::os_arrow_down: + coord.y += static_cast(line_px); + break; + case keyboard::os_home: + //move the caret to the begining of the line + pos.x = 0; + + //move the caret to the begining of the text if Ctrl is pressed + if (arg.ctrl) + pos.y = 0; + break; + case keyboard::os_end: + //move the caret to the end of the line + pos.x = static_cast(text_length); + + //move the caret to the end of the text if Ctrl is pressed + if (arg.ctrl) + pos.y = static_cast((line_count - 1) * line_px); + break; + case keyboard::os_pageup: + if (origin.y > 0) + { + auto off = coord - origin; + origin.y -= (std::min)(origin.y, static_cast(impl_->cview->view_area().height)); + coord = off + origin; + } + break; + case keyboard::os_pagedown: + if (impl_->cview->content_size().height > impl_->cview->view_area().height) + { + auto off = coord - origin; + origin.y = (std::min)(origin.y + static_cast(impl_->cview->view_area().height), static_cast(impl_->cview->content_size().height - impl_->cview->view_area().height)); + coord = off + origin; + } + break; + } + + if (pos == points_.caret) + { + impl_->cview->move_origin(origin - impl_->cview->origin()); + pos = _m_coordinate_to_caret(coord, false); + } + + if (pos != points_.caret) { + if (arg.shift) { + switch (key) { + case keyboard::os_arrow_left: + case keyboard::os_arrow_up: + case keyboard::os_home: + case keyboard::os_pageup: + select_.b = pos; + break; + case keyboard::os_arrow_right: + case keyboard::os_arrow_down: + case keyboard::os_end: + case keyboard::os_pagedown: + select_.b = pos; + break; + } + } + else { + select_.b = pos; + select_.a = pos; + } + points_.caret = pos; + impl_->try_refresh = sync_graph::refresh; + this->_m_adjust_view(); + impl_->cview->sync(true); + this->reset_caret(); + } + } + + unsigned text_editor::_m_width_px(bool include_vs) const + { + unsigned exclude_px = API::open_caret(window_, true).get()->dimension().width; + + if (!include_vs) + exclude_px += impl_->cview->space(); + + return (text_area_.area.width > exclude_px ? text_area_.area.width - exclude_px : 0); + } + + void text_editor::_m_draw_border() + { + if (!API::widget_borderless(this->window_)) + { + if (impl_->customized_renderers.border) + { + impl_->customized_renderers.border(graph_, _m_bgcolor()); + } + else + { + ::nana::facade facade; + facade.draw(graph_, _m_bgcolor(), API::fgcolor(this->window_), ::nana::rectangle{ API::window_size(this->window_) }, API::element_state(this->window_)); + } + + if (!attributes_.line_wrapped) + { + auto exclude_px = API::open_caret(window_, true).get()->dimension().width; + int x = this->text_area_.area.x + static_cast(width_pixels()); + graph_.rectangle(rectangle{ x, this->text_area_.area.y, exclude_px, text_area_.area.height }, true, _m_bgcolor()); } } - if((pos.y > a.y || (pos.y == a.y && pos.x >= a.x)) && ((pos.y < b.y) || (pos.y == b.y && pos.x < b.x))) - return true; + draw_corner(); } - return false; - } - bool text_editor::move_select() - { - if(hit_select_area(points_.caret, true) || (select_.b == points_.caret)) + const upoint& text_editor::mouse_caret(const point& scrpos, bool stay_in_view) //From screen position { - points_.caret = select_.b; + points_.caret = _m_coordinate_to_caret(scrpos); - if (this->_m_adjust_view()) + if (stay_in_view && this->_m_adjust_view()) impl_->try_refresh = sync_graph::refresh; reset_caret(); - return true; + return points_.caret; } - if (_m_move_select(true)) + const upoint& text_editor::caret() const { - textbase().text_changed(); - this->_m_adjust_view(); - impl_->try_refresh = sync_graph::refresh; - return true; + return points_.caret; } - return false; - } - bool text_editor::mask(wchar_t ch) - { - if (mask_char_ == ch) + point text_editor::caret_screen_pos() const + { + return _m_caret_to_coordinate(points_.caret); + } + + bool text_editor::scroll(bool upwards, bool vert) + { + impl_->cview->scroll(!upwards, !vert); return false; - - mask_char_ = ch; - return true; - } - - unsigned text_editor::width_pixels() const - { - unsigned exclude_px = API::open_caret(window_, true).get()->dimension().width; - - if (attributes_.line_wrapped) - exclude_px += impl_->cview->extra_space(false); - - return (text_area_.area.width > exclude_px ? text_area_.area.width - exclude_px : 0); - } - - window text_editor::window_handle() const - { - return window_; - } - - const std::vector& text_editor::text_position() const - { - return impl_->text_position; - } - - void text_editor::focus_behavior(text_focus_behavior behavior) - { - select_.behavior = behavior; - } - - void text_editor::select_behavior(bool move_to_end) - { - select_.move_to_end = move_to_end; - } - - std::size_t text_editor::line_count(bool text_lines) const - { - if (text_lines) - return textbase().lines(); - - 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_); - } - - void text_editor::render(bool has_focus) - { - const auto bgcolor = _m_bgcolor(); - - auto fgcolor = scheme_->foreground.get_color(); - if (!API::window_enabled(window_)) - fgcolor.blend(bgcolor, 0.5); - - if (API::widget_borderless(window_)) - graph_.rectangle(false, bgcolor); - - //Draw background - if (!API::dev::copy_transparent_background(window_, graph_)) - { - if (attributes_.enable_background) - graph_.rectangle(text_area_.area, true, bgcolor); } - if (impl_->customized_renderers.background) - impl_->customized_renderers.background(graph_, text_area_.area, bgcolor); - - if(impl_->counterpart.buffer && !text_area_.area.empty()) - impl_->counterpart.buffer.bitblt(rectangle{ text_area_.area.dimension() }, graph_, text_area_.area.position()); - - //Render the content when the text isn't empty or the window has got focus, - //otherwise draw the tip string. - if ((false == textbase().empty()) || has_focus) + color text_editor::_m_draw_colored_area(paint::graphics& graph, const std::pair& row, bool whole_line) { - auto text_pos = _m_render_text(fgcolor); - - if (text_pos.empty()) - text_pos.emplace_back(upoint{}); - - if ((impl_->text_position_origin != impl_->cview->origin().y) || (text_pos != impl_->text_position)) + auto area = impl_->colored_area.find(row.first); + if (area) { - impl_->text_position_origin = impl_->cview->origin().y; - impl_->text_position.swap(text_pos); - if (event_handler_) - event_handler_->text_exposed(impl_->text_position); - } - } - else //Draw tip string - { - graph_.string({ text_area_.area.x - impl_->cview->origin().x, text_area_.area.y }, attributes_.tip_string, static_cast(0x787878)); - } - - if (impl_->text_position.empty()) - impl_->text_position.emplace_back(upoint{}); - - _m_draw_border(); - impl_->try_refresh = sync_graph::none; - } - //public: - void text_editor::put(std::wstring text, bool perform_event) - { - if (text.empty()) - return; - - auto undo_ptr = std::unique_ptr{ new undo_input_text(*this, text) }; - - undo_ptr->set_selected_text(); - - //Do not forget to assign the _m_erase_select() to caret - //because _m_put() will insert the text at the position where the caret is. - points_.caret = _m_erase_select(false); - - undo_ptr->set_caret_pos(); - points_.caret = _m_put(std::move(text), false); - - impl_->undo.push(std::move(undo_ptr)); - - _m_reset_content_size(true); - if (perform_event) - textbase().text_changed(); - - if(graph_) - { - if(this->_m_adjust_view()) - impl_->cview->sync(false); - - reset_caret(); - impl_->try_refresh = sync_graph::refresh; - } - } - - void text_editor::put(wchar_t ch) - { - std::wstring ch_str(1, ch); - - auto undo_ptr = std::unique_ptr{new undo_input_text(*this, ch_str)}; - bool refresh = (select_.a != select_.b); - - undo_ptr->set_selected_text(); - if(refresh) - points_.caret = _m_erase_select(false); - - undo_ptr->set_caret_pos(); - - impl_->undo.push(std::move(undo_ptr)); - - auto secondary_before = impl_->capacities.behavior->take_lines(points_.caret.y); - textbase().insert(points_.caret, std::move(ch_str)); - _m_pre_calc_lines(points_.caret.y, 1); - - textbase().text_changed(); - - points_.caret.x ++; - - _m_reset_content_size(); - - if (!refresh) - { - if (!_m_update_caret_line(secondary_before)) - draw_corner(); - } - else - impl_->try_refresh = sync_graph::refresh; - - } - - void text_editor::copy() const - { - //Disallows copying text if the text_editor is masked. - if (mask_char_) - return; - - auto text = _m_make_select_string(); - if (!text.empty()) - nana::system::dataexch().set(text, API::root(this->window_)); - } - - void text_editor::cut() - { - copy(); - del(); - } - - void text_editor::paste() - { - auto text = system::dataexch{}.wget(); - - if ((accepts::no_restrict == impl_->capacities.acceptive) || !impl_->capacities.pred_acceptive) - { - put(move(text), true); - return; - } - - //Check if the input is acceptable - for (auto i = text.begin(); i != text.end(); ++i) - { - if (_m_accepts(*i)) - { - if (accepts::no_restrict == impl_->capacities.acceptive) - put(*i); - - continue; - } - - if (accepts::no_restrict != impl_->capacities.acceptive) - { - text.erase(i, text.end()); - put(move(text), true); - } - break; - } - } - - void text_editor::enter(bool record_undo, bool perform_event) - { - if(false == attributes_.multi_lines) - return; - - auto undo_ptr = std::unique_ptr(new undo_input_text(*this, std::wstring(1, '\n'))); - - undo_ptr->set_selected_text(); - points_.caret = _m_erase_select(false); - - undo_ptr->set_caret_pos(); - - auto & textbase = this->textbase(); - - const string_type& lnstr = textbase.getline(points_.caret.y); - ++points_.caret.y; - - if(lnstr.size() > points_.caret.x) - { - //Breaks the line and moves the rest part to a new line - auto rest_part_len = lnstr.size() - points_.caret.x; //Firstly get the length of rest part, because lnstr may be invalid after insertln - textbase.insertln(points_.caret.y, lnstr.substr(points_.caret.x)); - textbase.erase(points_.caret.y - 1, points_.caret.x, rest_part_len); - } - else - { - if (textbase.lines() == 0) - textbase.insertln(0, std::wstring{}); - textbase.insertln(points_.caret.y, std::wstring{}); - } - - if (record_undo) - impl_->undo.push(std::move(undo_ptr)); - - impl_->capacities.behavior->add_lines(points_.caret.y - 1, 1); - _m_pre_calc_lines(points_.caret.y - 1, 2); - - points_.caret.x = 0; - - auto origin = impl_->cview->origin(); - origin.x = 0; - - if (impl_->indent.enabled) - { - if (impl_->indent.generator) - { - put(nana::to_wstring(impl_->indent.generator()), false); - } - else - { - auto & text = textbase.getline(points_.caret.y - 1); - auto indent_pos = text.find_first_not_of(L"\t "); - if (indent_pos != std::wstring::npos) - put(text.substr(0, indent_pos), false); - else - put(text, false); - } - } - else - _m_reset_content_size(); - - if (perform_event) - textbase.text_changed(); - - auto origin_moved = impl_->cview->move_origin(origin - impl_->cview->origin()); - - if (this->_m_adjust_view() || origin_moved) - impl_->cview->sync(true); - } - - void text_editor::del() - { - if(select_.a == select_.b) - { - if(textbase().getline(points_.caret.y).size() > points_.caret.x) - { - ++points_.caret.x; - } - else if (points_.caret.y + 1 < textbase().lines()) - { //Move to next line - points_.caret.x = 0; - ++points_.caret.y; - } - else - return; //No characters behind the caret - } - - backspace(true, true); - } - - void text_editor::backspace(bool record_undo, bool perform_event) - { - auto undo_ptr = std::unique_ptr(new undo_backspace(*this)); - bool has_to_redraw = true; - if(select_.a == select_.b) - { - auto & textbase = this->textbase(); - if(points_.caret.x) - { - unsigned erase_number = 1; - --points_.caret.x; - - auto& lnstr = textbase.getline(points_.caret.y); - - undo_ptr->set_caret_pos(); - undo_ptr->set_removed(lnstr.substr(points_.caret.x, erase_number)); - auto secondary = impl_->capacities.behavior->take_lines(points_.caret.y); - textbase.erase(points_.caret.y, points_.caret.x, erase_number); - _m_pre_calc_lines(points_.caret.y, 1); - - if (!this->_m_adjust_view()) + if (!area->bgcolor.invisible()) { - _m_update_line(points_.caret.y, secondary); - has_to_redraw = false; + auto const height = line_height(); + + auto top = _m_caret_to_coordinate(upoint{ 0, static_cast(row.first) }).y; + std::size_t lines = 1; + + if (whole_line) + lines = impl_->capacities.behavior->take_lines(row.first); + else + top += static_cast(height * row.second); + + const rectangle area_r = { text_area_.area.x, top, width_pixels(), static_cast(height * lines) }; + + if (API::is_transparent_background(this->window_)) + graph.blend(area_r, area->bgcolor, 1); + else + graph.rectangle(area_r, true, area->bgcolor); } + + return area->fgcolor; } - else if (points_.caret.y) - { - points_.caret.x = static_cast(textbase.getline(--points_.caret.y).size()); - textbase.merge(points_.caret.y); - impl_->capacities.behavior->merge_lines(points_.caret.y, points_.caret.y + 1); - undo_ptr->set_caret_pos(); - undo_ptr->set_removed(std::wstring(1, '\n')); - } - else - undo_ptr.reset(); + return{}; } - else + + std::vector text_editor::_m_render_text(const color& text_color) { - undo_ptr->set_selected_text(); - points_.caret = _m_erase_select(false); - undo_ptr->set_caret_pos(); - } + std::vector line_indexes; + auto const behavior = this->impl_->capacities.behavior; + auto const line_count = textbase().lines(); - if (record_undo) - impl_->undo.push(std::move(undo_ptr)); + auto row = behavior->text_position_from_screen(impl_->cview->view_area().y); - _m_reset_content_size(false); + if (row.first >= line_count || graph_.empty()) + return line_indexes; - if (perform_event) - textbase().text_changed(); - - textbase().text_changed(); - - if(has_to_redraw) - { - this->_m_adjust_view(); - impl_->try_refresh = sync_graph::refresh; - } - } - - void text_editor::undo(bool reverse) - { - if (reverse) - impl_->undo.redo(); - else - impl_->undo.undo(); - - _m_reset_content_size(true); - - this->_m_adjust_view(); - impl_->try_refresh = sync_graph::refresh; - } - - void text_editor::set_undo_queue_length(std::size_t len) - { - impl_->undo.max_steps(len); - } - - void text_editor::move_ns(bool to_north) - { - const bool redraw_required = _m_cancel_select(0); - if (_m_move_caret_ns(to_north) || redraw_required) - impl_->try_refresh = sync_graph::refresh; - } - - void text_editor::move_left() - { - bool pending = true; - if(_m_cancel_select(1) == false) - { - if(points_.caret.x) + auto sections = behavior->line(row.first); + if (row.second < sections.size()) { - --points_.caret.x; - pending = false; - if (this->_m_adjust_view()) - impl_->try_refresh = sync_graph::refresh; - } - else if (points_.caret.y) //Move to previous line - points_.caret.x = static_cast(textbase().getline(--points_.caret.y).size()); - else - pending = false; - } + nana::upoint str_pos(0, static_cast(row.first)); + str_pos.x = static_cast(sections[row.second].begin - textbase().getline(row.first).c_str()); - if (pending && this->_m_adjust_view()) - impl_->try_refresh = sync_graph::refresh; - } + int top = _m_text_top_base() - (impl_->cview->origin().y % line_height()); + const unsigned pixels = line_height(); - void text_editor::move_right() - { - bool do_render = false; - if (_m_cancel_select(2) == false) - { - auto lnstr = textbase().getline(points_.caret.y); - if (lnstr.size() > points_.caret.x) - { - ++points_.caret.x; - do_render = this->_m_adjust_view(); - } - else if (points_.caret.y + 1 < textbase().lines()) - { //Move to next line - points_.caret.x = 0; - ++points_.caret.y; - do_render = this->_m_adjust_view(); - } - } - else - do_render = this->_m_adjust_view(); - - if (do_render) - impl_->try_refresh = sync_graph::refresh; - } - - void text_editor::_m_handle_move_key(const arg_keyboard& arg) - { - if (arg.shift && (select_.a == select_.b)) - select_.a = select_.b = points_.caret; - - auto origin = impl_->cview->origin(); - auto pos = points_.caret; - auto coord = _m_caret_to_coordinate(points_.caret, false); - - wchar_t key = arg.key; - - auto const line_px = this->line_height(); - - //The number of text lines - auto const line_count = textbase().lines(); - - //The number of charecters in the line of caret - auto const text_length = textbase().getline(points_.caret.y).size(); - - - switch (key) { - case keyboard::os_arrow_left: - if (select_.move_to_end && (select_.a != select_.b) && (!arg.shift)) - { - pos = select_.a; - } - else if (pos.x != 0) - { - --pos.x; - } - else if (pos.y != 0) { - --pos.y; - pos.x = static_cast(textbase().getline(pos.y).size()); - } - break; - case keyboard::os_arrow_right: - if (select_.move_to_end && (select_.a != select_.b) && (!arg.shift)) - { - pos = select_.b; - } - else if (pos.x < text_length) - { - ++pos.x; - } - else if (pos.y != line_count - 1) - { - ++pos.y; - pos.x = 0; - } - break; - case keyboard::os_arrow_up: - coord.y -= static_cast(line_px); - break; - case keyboard::os_arrow_down: - coord.y += static_cast(line_px); - break; - case keyboard::os_home: - //move the caret to the begining of the line - pos.x = 0; - - //move the caret to the begining of the text if Ctrl is pressed - if (arg.ctrl) - pos.y = 0; - break; - case keyboard::os_end: - //move the caret to the end of the line - pos.x = static_cast(text_length); - - //move the caret to the end of the text if Ctrl is pressed - if(arg.ctrl) - pos.y = static_cast((line_count - 1) * line_px); - break; - case keyboard::os_pageup: - if(origin.y > 0) - { - auto off = coord - origin; - origin.y -= (std::min)(origin.y, static_cast(impl_->cview->view_area().height)); - coord = off + origin; - } - break; - case keyboard::os_pagedown: - if (impl_->cview->content_size().height > impl_->cview->view_area().height) - { - auto off = coord - origin; - origin.y = (std::min)(origin.y + static_cast(impl_->cview->view_area().height), static_cast(impl_->cview->content_size().height - impl_->cview->view_area().height)); - coord = off + origin; - } - break; - } - - if (pos == points_.caret) - { - impl_->cview->move_origin(origin - impl_->cview->origin()); - pos = _m_coordinate_to_caret(coord, false); - } - - if (pos != points_.caret) { - if (arg.shift) { - switch (key) { - case keyboard::os_arrow_left: - case keyboard::os_arrow_up: - case keyboard::os_home: - case keyboard::os_pageup: - select_.b = pos; - break; - case keyboard::os_arrow_right: - case keyboard::os_arrow_down: - case keyboard::os_end: - case keyboard::os_pagedown: - select_.b = pos; - break; - } - } - else { - select_.b = pos; - select_.a = pos; - } - points_.caret = pos; - impl_->try_refresh = sync_graph::refresh; - this->_m_adjust_view(); - impl_->cview->sync(true); - this->reset_caret(); - } - } - - unsigned text_editor::_m_width_px(bool include_vs) const - { - unsigned exclude_px = API::open_caret(window_, true).get()->dimension().width; - - if (!include_vs) - exclude_px += impl_->cview->space(); - - return (text_area_.area.width > exclude_px ? text_area_.area.width - exclude_px : 0); - } - - void text_editor::_m_draw_border() - { - if (!API::widget_borderless(this->window_)) - { - if (impl_->customized_renderers.border) - { - impl_->customized_renderers.border(graph_, _m_bgcolor()); - } - else - { - ::nana::facade facade; - facade.draw(graph_, _m_bgcolor(), API::fgcolor(this->window_), ::nana::rectangle{ API::window_size(this->window_) }, API::element_state(this->window_)); - } - - if (!attributes_.line_wrapped) - { - auto exclude_px = API::open_caret(window_, true).get()->dimension().width; - int x = this->text_area_.area.x + static_cast(width_pixels()); - graph_.rectangle(rectangle{ x, this->text_area_.area.y, exclude_px, text_area_.area.height }, true, _m_bgcolor()); - } - } - - draw_corner(); - } - - const upoint& text_editor::mouse_caret(const point& scrpos, bool stay_in_view) //From screen position - { - points_.caret = _m_coordinate_to_caret(scrpos); - - if (stay_in_view && this->_m_adjust_view()) - impl_->try_refresh = sync_graph::refresh; - - reset_caret(); - return points_.caret; - } - - const upoint& text_editor::caret() const - { - return points_.caret; - } - - point text_editor::caret_screen_pos() const - { - return _m_caret_to_coordinate(points_.caret); - } - - bool text_editor::scroll(bool upwards, bool vert) - { - impl_->cview->scroll(!upwards, !vert); - return false; - } - - color text_editor::_m_draw_colored_area(paint::graphics& graph, const std::pair& row, bool whole_line) - { - auto area = impl_->colored_area.find(row.first); - if (area) - { - if (!area->bgcolor.invisible()) - { - auto const height = line_height(); - - auto top = _m_caret_to_coordinate(upoint{ 0, static_cast(row.first) }).y; - std::size_t lines = 1; - - if (whole_line) - lines = impl_->capacities.behavior->take_lines(row.first); - else - top += static_cast(height * row.second); - - const rectangle area_r = { text_area_.area.x, top, width_pixels(), static_cast(height * lines) }; - - if (API::is_transparent_background(this->window_)) - graph.blend(area_r, area->bgcolor, 1); - else - graph.rectangle(area_r, true, area->bgcolor); - } - - return area->fgcolor; - } - return{}; - } - - std::vector text_editor::_m_render_text(const color& text_color) - { - std::vector line_indexes; - auto const behavior = this->impl_->capacities.behavior; - auto const line_count = textbase().lines(); - - auto row = behavior->text_position_from_screen(impl_->cview->view_area().y); - - if (row.first >= line_count || graph_.empty()) - return line_indexes; - - auto sections = behavior->line(row.first); - if (row.second < sections.size()) - { - nana::upoint str_pos(0, static_cast(row.first)); - str_pos.x = static_cast(sections[row.second].begin - textbase().getline(row.first).c_str()); - - int top = _m_text_top_base() - (impl_->cview->origin().y % line_height()); - const unsigned pixels = line_height(); - - const std::size_t scrlines = screen_lines() + 1; - for (std::size_t pos = 0; pos < scrlines; ++pos, top += pixels) - { - if (row.first >= line_count) - break; - - auto fgcolor = _m_draw_colored_area(graph_, row, false); - if (fgcolor.invisible()) - fgcolor = text_color; - - sections = behavior->line(row.first); - if (row.second < sections.size()) + const std::size_t scrlines = screen_lines() + 1; + for (std::size_t pos = 0; pos < scrlines; ++pos, top += pixels) { - auto const & sct = sections[row.second]; + if (row.first >= line_count) + break; - _m_draw_string(top, fgcolor, str_pos, sct, true); - line_indexes.emplace_back(str_pos); - ++row.second; - if (row.second >= sections.size()) + auto fgcolor = _m_draw_colored_area(graph_, row, false); + if (fgcolor.invisible()) + fgcolor = text_color; + + sections = behavior->line(row.first); + if (row.second < sections.size()) { - ++row.first; - row.second = 0; - str_pos.x = 0; - ++str_pos.y; + auto const & sct = sections[row.second]; + + _m_draw_string(top, fgcolor, str_pos, sct, true); + line_indexes.emplace_back(str_pos); + ++row.second; + if (row.second >= sections.size()) + { + ++row.first; + row.second = 0; + str_pos.x = 0; + ++str_pos.y; + } + else + str_pos.x += static_cast(sct.end - sct.begin); } else - str_pos.x += static_cast(sct.end - sct.begin); + break; } - else - break; } + + return line_indexes; } - return line_indexes; - } - - void text_editor::_m_pre_calc_lines(std::size_t line_off, std::size_t lines) - { - unsigned width_px = width_pixels(); - for (auto pos = line_off, end = line_off + lines; pos != end; ++pos) - this->impl_->capacities.behavior->pre_calc_line(pos, width_px); - } - - nana::point text_editor::_m_caret_to_coordinate(nana::upoint pos, bool to_screen_coordinate) const - { - auto const behavior = impl_->capacities.behavior; - auto const sections = behavior->line(pos.y); - - std::size_t lines = 0; //lines before the caret line; - for (std::size_t i = 0; i < pos.y; ++i) + void text_editor::_m_pre_calc_lines(std::size_t line_off, std::size_t lines) { - lines += behavior->take_lines(i); + unsigned width_px = width_pixels(); + for (auto pos = line_off, end = line_off + lines; pos != end; ++pos) + this->impl_->capacities.behavior->pre_calc_line(pos, width_px); } - const text_section * sct_ptr = nullptr; - nana::point scrpos; - if (0 != pos.x) + nana::point text_editor::_m_caret_to_coordinate(nana::upoint pos, bool to_screen_coordinate) const { - std::wstring str; + auto const behavior = impl_->capacities.behavior; + auto const sections = behavior->line(pos.y); - std::size_t sct_pos = 0; - for (auto & sct : sections) + std::size_t lines = 0; //lines before the caret line; + for (std::size_t i = 0; i < pos.y; ++i) { - std::size_t chsize = sct.end - sct.begin; - str.clear(); - if (mask_char_) - str.append(chsize, mask_char_); - else - str.append(sct.begin, sct.end); + lines += behavior->take_lines(i); + } - //In line-wrapped mode. If the caret is at the end of a line which is not the last section, - //the caret should be moved to the beginning of next section line. - if ((sct_pos + 1 < sections.size()) ? (pos.x < chsize) : (pos.x <= chsize)) + const text_section * sct_ptr = nullptr; + nana::point scrpos; + if (0 != pos.x) + { + std::wstring str; + + std::size_t sct_pos = 0; + for (auto & sct : sections) { - sct_ptr = &sct; - if (pos.x == chsize) - scrpos.x = _m_text_extent_size(str.c_str(), sct.end - sct.begin).width; + std::size_t chsize = sct.end - sct.begin; + str.clear(); + if (mask_char_) + str.append(chsize, mask_char_); else - scrpos.x = _m_pixels_by_char(str, pos.x); - break; + str.append(sct.begin, sct.end); + + //In line-wrapped mode. If the caret is at the end of a line which is not the last section, + //the caret should be moved to the beginning of next section line. + if ((sct_pos + 1 < sections.size()) ? (pos.x < chsize) : (pos.x <= chsize)) + { + sct_ptr = &sct; + if (pos.x == chsize) + scrpos.x = _m_text_extent_size(str.c_str(), sct.end - sct.begin).width; + else + scrpos.x = _m_pixels_by_char(str, pos.x); + break; + } + else + { + pos.x -= static_cast(chsize); + ++lines; + } + + ++sct_pos; } + } + + if (!sct_ptr) + { + if (sections.empty()) + scrpos.x += _m_text_x({}); else - { - pos.x -= static_cast(chsize); - ++lines; - } - - ++sct_pos; + scrpos.x += _m_text_x(sections.front()); } + else + scrpos.x += _m_text_x(*sct_ptr); + + scrpos.y = static_cast(lines * line_height()); + + if (!to_screen_coordinate) + { + //_m_text_x includes origin x and text_area x. remove these factors + scrpos.x += (impl_->cview->origin().x - text_area_.area.x); + } + else + scrpos.y += this->_m_text_top_base() - impl_->cview->origin().y; + + return scrpos; } - if (!sct_ptr) + upoint text_editor::_m_coordinate_to_caret(point scrpos, bool from_screen_coordinate) const { + if (!from_screen_coordinate) + scrpos -= (impl_->cview->origin() - point{ text_area_.area.x, this->_m_text_top_base() }); + + auto const behavior = impl_->capacities.behavior; + + auto const row = behavior->text_position_from_screen(scrpos.y); + + auto sections = behavior->line(row.first); if (sections.empty()) - scrpos.x += _m_text_x({}); - else - scrpos.x += _m_text_x(sections.front()); - } - else - scrpos.x += _m_text_x(*sct_ptr); + return{ 0, static_cast(row.first) }; - scrpos.y = static_cast(lines * line_height()); + //First of all, find the text of secondary. + auto real_str = sections[row.second]; - if (!to_screen_coordinate) - { - //_m_text_x includes origin x and text_area x. remove these factors - scrpos.x += (impl_->cview->origin().x - text_area_.area.x); - } - else - scrpos.y += this->_m_text_top_base() - impl_->cview->origin().y; + auto text_ptr = real_str.begin; + const auto text_size = real_str.end - real_str.begin; - return scrpos; - } - - upoint text_editor::_m_coordinate_to_caret(point scrpos, bool from_screen_coordinate) const - { - if (!from_screen_coordinate) - scrpos -= (impl_->cview->origin() - point{ text_area_.area.x, this->_m_text_top_base() }); - - auto const behavior = impl_->capacities.behavior; - - auto const row = behavior->text_position_from_screen(scrpos.y); - - auto sections = behavior->line(row.first); - if (sections.empty()) - return{ 0, static_cast(row.first) }; - - //First of all, find the text of secondary. - auto real_str = sections[row.second]; - - auto text_ptr = real_str.begin; - const auto text_size = real_str.end - real_str.begin; - - std::wstring mask_str; - if (mask_char_) - { - mask_str.resize(text_size, mask_char_); - text_ptr = mask_str.c_str(); - } - - auto const reordered = unicode_reorder(text_ptr, text_size); - - nana::upoint res(static_cast(real_str.begin - sections.front().begin), static_cast(row.first)); - - scrpos.x = (std::max)(0, (scrpos.x - _m_text_x(sections[row.second]))); - - for (auto & ent : reordered) - { - auto str_px = static_cast(_m_text_extent_size(ent.begin, ent.end - ent.begin).width); - if (scrpos.x <= str_px) + std::wstring mask_str; + if (mask_char_) { - res.x += _m_char_by_pixels(ent, scrpos.x) + static_cast(ent.begin - text_ptr); - return res; + mask_str.resize(text_size, mask_char_); + text_ptr = mask_str.c_str(); } - scrpos.x -= str_px; - } - //move the caret to the end of this section. - res.x = static_cast(text_size); - for (std::size_t i = 0; i < row.second; ++i) - res.x += static_cast(sections[i].end - sections[i].begin); + auto const reordered = unicode_reorder(text_ptr, text_size); - return res; - } + nana::upoint res(static_cast(real_str.begin - sections.front().begin), static_cast(row.first)); - bool text_editor::_m_pos_from_secondary(std::size_t textline, const nana::upoint& secondary, unsigned & pos) - { - if (textline >= textbase().lines()) - return false; + scrpos.x = (std::max)(0, (scrpos.x - _m_text_x(sections[row.second]))); - auto sections = impl_->capacities.behavior->line(textline); - - if (secondary.y >= sections.size()) - return false; - - auto const & sct = sections[secondary.y]; - - auto chptr = sct.begin + (std::min)(secondary.x, static_cast(sct.end - sct.begin)); - pos = static_cast(chptr - textbase().getline(textline).c_str()); - return true; - } - - - bool text_editor::_m_pos_secondary(const nana::upoint& charpos, nana::upoint& secondary_pos) const - { - if (charpos.y >= textbase().lines()) - return false; - - secondary_pos.x = charpos.x; - secondary_pos.y = 0; - - auto sections = impl_->capacities.behavior->line(charpos.y); - unsigned len = 0; - for (auto & sct : sections) - { - len = static_cast(sct.end - sct.begin); - if (len >= secondary_pos.x) - return true; - - ++secondary_pos.y; - secondary_pos.x -= len; - } - --secondary_pos.y; - secondary_pos.x = len; - return true; - } - - bool text_editor::_m_move_caret_ns(bool to_north) - { - auto const behavior = impl_->capacities.behavior; - - nana::upoint secondary_pos; - _m_pos_secondary(points_.caret, secondary_pos); - - if (to_north) //North - { - if (0 == secondary_pos.y) + for (auto & ent : reordered) { - if (0 == points_.caret.y) - return false; - - --points_.caret.y; - secondary_pos.y = static_cast(behavior->take_lines(points_.caret.y)) - 1; - } - else - { - //Test if out of screen - if (static_cast(points_.caret.y) < _m_text_topline()) + auto str_px = static_cast(_m_text_extent_size(ent.begin, ent.end - ent.begin).width); + if (scrpos.x <= str_px) { - auto origin = impl_->cview->origin(); - origin.y = static_cast(points_.caret.y) * line_height(); - impl_->cview->move_origin(origin - impl_->cview->origin()); + res.x += _m_char_by_pixels(ent, scrpos.x) + static_cast(ent.begin - text_ptr); + return res; } - - --secondary_pos.y; + scrpos.x -= str_px; } - } - else //South - { - ++secondary_pos.y; - if (secondary_pos.y >= behavior->take_lines(points_.caret.y)) - { - secondary_pos.y = 0; - if (points_.caret.y + 1 >= textbase().lines()) - return false; - ++points_.caret.y; - } + //move the caret to the end of this section. + res.x = static_cast(text_size); + for (std::size_t i = 0; i < row.second; ++i) + res.x += static_cast(sections[i].end - sections[i].begin); + + return res; } - _m_pos_from_secondary(points_.caret.y, secondary_pos, points_.caret.x); - return this->_m_adjust_view(); - } - - void text_editor::_m_update_line(std::size_t pos, std::size_t secondary_count_before) - { - auto behavior = impl_->capacities.behavior; - if (behavior->take_lines(pos) == secondary_count_before) + bool text_editor::_m_pos_from_secondary(std::size_t textline, const nana::upoint& secondary, unsigned & pos) { - auto top = _m_caret_to_coordinate(upoint{ 0, static_cast(pos) }).y; + if (textline >= textbase().lines()) + return false; - const unsigned pixels = line_height(); - const rectangle update_area = { text_area_.area.x, top, width_pixels(), static_cast(pixels * secondary_count_before) }; + auto sections = impl_->capacities.behavior->line(textline); - if (!API::dev::copy_transparent_background(window_, update_area, graph_, update_area.position())) - { - _m_draw_colored_area(graph_, { pos, 0 }, true); - graph_.rectangle(update_area, true, API::bgcolor(window_)); - } - else - _m_draw_colored_area(graph_, { pos, 0 }, true); + if (secondary.y >= sections.size()) + return false; - auto fgcolor = API::fgcolor(window_); - auto text_ptr = textbase().getline(pos).c_str(); + auto const & sct = sections[secondary.y]; - auto sections = behavior->line(pos); - for (auto & sct : sections) - { - _m_draw_string(top, fgcolor, nana::upoint(static_cast(sct.begin - text_ptr), points_.caret.y), sct, true); - top += pixels; - } - - _m_draw_border(); - impl_->try_refresh = sync_graph::lazy_refresh; - } - else - impl_->try_refresh = sync_graph::refresh; - } - - bool text_editor::_m_accepts(char_type ch) const - { - if (accepts::no_restrict == impl_->capacities.acceptive) - { - if (impl_->capacities.pred_acceptive) - return impl_->capacities.pred_acceptive(ch); + auto chptr = sct.begin + (std::min)(secondary.x, static_cast(sct.end - sct.begin)); + pos = static_cast(chptr - textbase().getline(textline).c_str()); return true; } - //Checks the input whether it meets the requirement for a numeric. - auto str = text(); - if ('+' == ch || '-' == ch) - return str.empty(); - - if((accepts::real == impl_->capacities.acceptive) && ('.' == ch)) - return (str.find(L'.') == str.npos); - - return ('0' <= ch && ch <= '9'); - } - - ::nana::color text_editor::_m_bgcolor() const - { - return (!API::window_enabled(window_) ? static_cast(0xE0E0E0) : API::bgcolor(window_)); - } - - void text_editor::_m_reset_content_size(bool calc_lines) - { - size csize; - - if (this->attributes_.line_wrapped) + bool text_editor::_m_pos_secondary(const nana::upoint& charpos, nana::upoint& secondary_pos) const { - //detect if vertical scrollbar is required - auto const max_lines = screen_lines(true); + if (charpos.y >= textbase().lines()) + return false; - if (calc_lines) + secondary_pos.x = charpos.x; + secondary_pos.y = 0; + + auto sections = impl_->capacities.behavior->line(charpos.y); + unsigned len = 0; + for (auto & sct : sections) { - auto text_lines = textbase().lines(); - if (text_lines <= max_lines) + len = static_cast(sct.end - sct.begin); + if (len >= secondary_pos.x) + return true; + + ++secondary_pos.y; + secondary_pos.x -= len; + } + --secondary_pos.y; + secondary_pos.x = len; + return true; + } + + bool text_editor::_m_move_caret_ns(bool to_north) + { + auto const behavior = impl_->capacities.behavior; + + nana::upoint secondary_pos; + _m_pos_secondary(points_.caret, secondary_pos); + + if (to_north) //North + { + if (0 == secondary_pos.y) { - impl_->capacities.behavior->prepare(); + if (0 == points_.caret.y) + return false; - auto const width_px = _m_width_px(true); - - std::size_t lines = 0; - for (std::size_t i = 0; i < text_lines; ++i) + --points_.caret.y; + secondary_pos.y = static_cast(behavior->take_lines(points_.caret.y)) - 1; + } + else + { + //Test if out of screen + if (static_cast(points_.caret.y) < _m_text_topline()) { - impl_->capacities.behavior->pre_calc_line(i, width_px); - lines += impl_->capacities.behavior->take_lines(i); + auto origin = impl_->cview->origin(); + origin.y = static_cast(points_.caret.y) * line_height(); + impl_->cview->move_origin(origin - impl_->cview->origin()); + } - if (lines > max_lines) + --secondary_pos.y; + } + } + else //South + { + ++secondary_pos.y; + if (secondary_pos.y >= behavior->take_lines(points_.caret.y)) + { + secondary_pos.y = 0; + if (points_.caret.y + 1 >= textbase().lines()) + return false; + + ++points_.caret.y; + } + } + + _m_pos_from_secondary(points_.caret.y, secondary_pos, points_.caret.x); + return this->_m_adjust_view(); + } + + void text_editor::_m_update_line(std::size_t pos, std::size_t secondary_count_before) + { + auto behavior = impl_->capacities.behavior; + if (behavior->take_lines(pos) == secondary_count_before) + { + auto top = _m_caret_to_coordinate(upoint{ 0, static_cast(pos) }).y; + + const unsigned pixels = line_height(); + const rectangle update_area = { text_area_.area.x, top, width_pixels(), static_cast(pixels * secondary_count_before) }; + + if (!API::dev::copy_transparent_background(window_, update_area, graph_, update_area.position())) + { + _m_draw_colored_area(graph_, { pos, 0 }, true); + graph_.rectangle(update_area, true, API::bgcolor(window_)); + } + else + _m_draw_colored_area(graph_, { pos, 0 }, true); + + auto fgcolor = API::fgcolor(window_); + auto text_ptr = textbase().getline(pos).c_str(); + + auto sections = behavior->line(pos); + for (auto & sct : sections) + { + _m_draw_string(top, fgcolor, nana::upoint(static_cast(sct.begin - text_ptr), points_.caret.y), sct, true); + top += pixels; + } + + _m_draw_border(); + impl_->try_refresh = sync_graph::lazy_refresh; + } + else + impl_->try_refresh = sync_graph::refresh; + } + + bool text_editor::_m_accepts(char_type ch) const + { + if (accepts::no_restrict == impl_->capacities.acceptive) + { + if (impl_->capacities.pred_acceptive) + return impl_->capacities.pred_acceptive(ch); + return true; + } + + //Checks the input whether it meets the requirement for a numeric. + auto str = text(); + + if ('+' == ch || '-' == ch) + return str.empty(); + + if ((accepts::real == impl_->capacities.acceptive) && ('.' == ch)) + return (str.find(L'.') == str.npos); + + return ('0' <= ch && ch <= '9'); + } + + ::nana::color text_editor::_m_bgcolor() const + { + return (!API::window_enabled(window_) ? static_cast(0xE0E0E0) : API::bgcolor(window_)); + } + + void text_editor::_m_reset_content_size(bool calc_lines) + { + size csize; + + if (this->attributes_.line_wrapped) + { + //detect if vertical scrollbar is required + auto const max_lines = screen_lines(true); + + if (calc_lines) + { + auto text_lines = textbase().lines(); + if (text_lines <= max_lines) + { + impl_->capacities.behavior->prepare(); + + auto const width_px = _m_width_px(true); + + std::size_t lines = 0; + for (std::size_t i = 0; i < text_lines; ++i) { - text_lines = lines; - break; + impl_->capacities.behavior->pre_calc_line(i, width_px); + lines += impl_->capacities.behavior->take_lines(i); + + if (lines > max_lines) + { + text_lines = lines; + break; + } } } + + //enable vertical scrollbar when text_lines > max_lines + csize.width = _m_width_px(text_lines <= max_lines); + impl_->capacities.behavior->pre_calc_lines(csize.width); + } + else + { + csize.width = impl_->cview->content_size().width; } - - //enable vertical scrollbar when text_lines > max_lines - csize.width = _m_width_px(text_lines <= max_lines); - impl_->capacities.behavior->pre_calc_lines(csize.width); } else { - csize.width = impl_->cview->content_size().width; - } - } - else - { - if (calc_lines) - impl_->capacities.behavior->pre_calc_lines(width_pixels()); + if (calc_lines) + impl_->capacities.behavior->pre_calc_lines(width_pixels()); - auto maxline = textbase().max_line(); - csize.width = _m_text_extent_size(textbase().getline(maxline.first).c_str(), maxline.second).width + caret_size().width; - } - - csize.height = static_cast(impl_->capacities.behavior->take_lines() * line_height()); - - impl_->cview->content_size(csize); - } - - void text_editor::_m_reset() - { - points_.caret.x = points_.caret.y = 0; - impl_->cview->move_origin(point{} -impl_->cview->origin()); - select_.a = select_.b; - } - - nana::upoint text_editor::_m_put(std::wstring text, bool perform_event) - { - auto & textbase = this->textbase(); - auto crtpos = points_.caret; - std::vector> lines; - if (_m_resolve_text(text, lines) && attributes_.multi_lines) - { - auto str_orig = textbase.getline(crtpos.y); - - auto const subpos = lines.front(); - auto substr = text.substr(subpos.first, subpos.second - subpos.first); - - if (str_orig.size() == crtpos.x) - textbase.insert(crtpos, std::move(substr)); - else - textbase.replace(crtpos.y, str_orig.substr(0, crtpos.x) + substr); - - //There are at least 2 elements in lines - for (auto i = lines.begin() + 1, end = lines.end() - 1; i != end; ++i) - { - textbase.insertln(++crtpos.y, text.substr(i->first, i->second - i->first)); + auto maxline = textbase().max_line(); + csize.width = _m_text_extent_size(textbase().getline(maxline.first).c_str(), maxline.second).width + caret_size().width; } - auto backpos = lines.back(); - textbase.insertln(++crtpos.y, text.substr(backpos.first, backpos.second - backpos.first) + str_orig.substr(crtpos.x)); - crtpos.x = static_cast(backpos.second - backpos.first); + csize.height = static_cast(impl_->capacities.behavior->take_lines() * line_height()); - impl_->capacities.behavior->add_lines(points_.caret.y, lines.size() - 1); - _m_pre_calc_lines(points_.caret.y, lines.size()); + impl_->cview->content_size(csize); } - else + + void text_editor::_m_reset() { - //Just insert the first line of text if the text is multilines. - if (lines.size() > 1) - text = text.substr(lines.front().first, lines.front().second - lines.front().first); - - crtpos.x += static_cast(text.size()); - textbase.insert(points_.caret, std::move(text)); - - _m_pre_calc_lines(crtpos.y, 1); + points_.caret.x = points_.caret.y = 0; + impl_->cview->move_origin(point{} -impl_->cview->origin()); + select_.a = select_.b; } - if (perform_event) - textbase.text_changed(); - - return crtpos; - } - - nana::upoint text_editor::_m_erase_select(bool perform_event) - { - nana::upoint a, b; - if (get_selected_points(a, b)) + nana::upoint text_editor::_m_put(std::wstring text, bool perform_event) { auto & textbase = this->textbase(); - if(a.y != b.y) + auto crtpos = points_.caret; + std::vector> lines; + if (_m_resolve_text(text, lines) && attributes_.multi_lines) { - textbase.erase(a.y, a.x, std::wstring::npos); - textbase.erase(a.y + 1, b.y - a.y - 1); + auto str_orig = textbase.getline(crtpos.y); - textbase.erase(a.y + 1, 0, b.x); - textbase.merge(a.y); + auto const subpos = lines.front(); + auto substr = text.substr(subpos.first, subpos.second - subpos.first); - impl_->capacities.behavior->merge_lines(a.y, b.y); + if (str_orig.size() == crtpos.x) + textbase.insert(crtpos, std::move(substr)); + else + textbase.replace(crtpos.y, str_orig.substr(0, crtpos.x) + substr); + + //There are at least 2 elements in lines + for (auto i = lines.begin() + 1, end = lines.end() - 1; i != end; ++i) + { + textbase.insertln(++crtpos.y, text.substr(i->first, i->second - i->first)); + } + + auto backpos = lines.back(); + textbase.insertln(++crtpos.y, text.substr(backpos.first, backpos.second - backpos.first) + str_orig.substr(crtpos.x)); + crtpos.x = static_cast(backpos.second - backpos.first); + + impl_->capacities.behavior->add_lines(points_.caret.y, lines.size() - 1); + _m_pre_calc_lines(points_.caret.y, lines.size()); } else { - textbase.erase(a.y, a.x, b.x - a.x); - _m_pre_calc_lines(a.y, 1); + //Just insert the first line of text if the text is multilines. + if (lines.size() > 1) + text = text.substr(lines.front().first, lines.front().second - lines.front().first); + + crtpos.x += static_cast(text.size()); + textbase.insert(points_.caret, std::move(text)); + + _m_pre_calc_lines(crtpos.y, 1); } if (perform_event) textbase.text_changed(); - select_.a = select_.b; - return a; + return crtpos; } - return points_.caret; - } - - std::wstring text_editor::_m_make_select_string() const - { - std::wstring text; - - nana::upoint a, b; - if (get_selected_points(a, b)) + nana::upoint text_editor::_m_erase_select(bool perform_event) { - auto & textbase = this->textbase(); - - if (a.y != b.y) + nana::upoint a, b; + if (get_selected_points(a, b)) { - text = textbase.getline(a.y).substr(a.x); - text += L"\r\n"; - for (unsigned i = a.y + 1; i < b.y; ++i) + auto & textbase = this->textbase(); + if (a.y != b.y) { - text += textbase.getline(i); - text += L"\r\n"; + textbase.erase(a.y, a.x, std::wstring::npos); + textbase.erase(a.y + 1, b.y - a.y - 1); + + textbase.erase(a.y + 1, 0, b.x); + textbase.merge(a.y); + + impl_->capacities.behavior->merge_lines(a.y, b.y); } - text += textbase.getline(b.y).substr(0, b.x); - } - else - return textbase.getline(a.y).substr(a.x, b.x - a.x); - } - - return text; - } - - std::size_t eat_endl(const wchar_t* str, std::size_t pos) - { - auto ch = str[pos]; - if (0 == ch) - return pos; - - const wchar_t * endlstr; - switch (ch) - { - case L'\n': - endlstr = L"\n\r"; - break; - case L'\r': - endlstr = L"\r\n"; - break; - default: - return pos; - } - - if (std::memcmp(str + pos, endlstr, sizeof(wchar_t) * 2) == 0) - return pos + 2; - - return pos + 1; - } - - bool text_editor::_m_resolve_text(const std::wstring& text, std::vector> & lines) - { - auto const text_str = text.c_str(); - std::size_t begin = 0; - while (true) - { - auto pos = text.find_first_of(L"\r\n", begin); - if (text.npos == pos) - { - if (!lines.empty()) - lines.emplace_back(begin, text.size()); - break; - } - - lines.emplace_back(begin, pos); - - pos = eat_endl(text_str, pos); - - begin = text.find_first_not_of(L"\r\n", pos); - - //The number of new lines minus one - const auto chp_end = text_str + (begin == text.npos ? text.size() : begin); - - for (auto chp = text_str + pos; chp != chp_end; ++chp) - { - auto eats = eat_endl(chp, 0); - if (eats) + else { - lines.emplace_back(); - chp += (eats - 1); + textbase.erase(a.y, a.x, b.x - a.x); + _m_pre_calc_lines(a.y, 1); } + + if (perform_event) + textbase.text_changed(); + + select_.a = select_.b; + return a; } - if (text.npos == begin) - { - lines.emplace_back(); - break; - } - } - return !lines.empty(); - } - - bool text_editor::_m_cancel_select(int align) - { - upoint a, b; - if (get_selected_points(a, b)) - { - //domain of algin = [0, 2] - if (align) - { - this->points_.caret = (1 == align ? a : b); - this->_m_adjust_view(); - } - - select_.a = select_.b = points_.caret; - reset_caret(); - return true; - } - return false; - } - - nana::size text_editor::_m_text_extent_size(const char_type* str, size_type n) const - { - if(mask_char_) - { - std::wstring maskstr; - maskstr.append(n, mask_char_); - return graph_.text_extent_size(maskstr); - } -#ifdef _nana_std_has_string_view - return graph_.text_extent_size(std::basic_string_view(str, n)); -#else - return graph_.text_extent_size(str, static_cast(n)); -#endif - } - - bool text_editor::_m_adjust_view() - { - auto const view_area = impl_->cview->view_area(); - - auto const line_px = static_cast(this->line_height()); - auto coord = _m_caret_to_coordinate(points_.caret, true); - - if (view_area.is_hit(coord) && view_area.is_hit({coord.x, coord.y + line_px})) - return false; - - unsigned extra_count_horz = 4; - unsigned extra_count_vert = 0; - - auto const origin = impl_->cview->origin(); - coord = _m_caret_to_coordinate(points_.caret, false); - - point moved_origin; - - //adjust x-axis if it isn't line-wrapped mode - if (!attributes_.line_wrapped) - { - auto extra = points_.caret; - - if (coord.x < origin.x) - { - extra.x -= (std::min)(extra_count_horz, points_.caret.x); - moved_origin.x = _m_caret_to_coordinate(extra, false).x - origin.x; - } - else if (coord.x + static_cast(caret_size().width) >= origin.x + static_cast(view_area.width)) - { - extra.x = (std::min)(static_cast(textbase().getline(points_.caret.y).size()), points_.caret.x + extra_count_horz); - auto new_origin = _m_caret_to_coordinate(extra, false).x + static_cast(caret_size().width) - static_cast(view_area.width); - moved_origin.x = new_origin - origin.x; - } + return points_.caret; } - auto extra_px = static_cast(line_px * extra_count_vert); - - if (coord.y < origin.y) + std::wstring text_editor::_m_make_select_string() const { - //Top of caret is less than the top of view - - moved_origin.y = (std::max)(0, coord.y - extra_px) - origin.y; - } - else if (coord.y + line_px >= origin.y + static_cast(view_area.height)) - { - //Bottom of caret is greater than the bottom of view - - auto const bottom = static_cast(impl_->capacities.behavior->take_lines() * line_px); - auto new_origin = (std::min)(coord.y + line_px + extra_px, bottom) - static_cast(view_area.height); - moved_origin.y = new_origin - origin.y; - } - - return impl_->cview->move_origin(moved_origin); - } - - bool text_editor::_m_move_select(bool record_undo) - { - if (!attributes_.editable) - return false; - - nana::upoint caret = points_.caret; - const auto text = _m_make_select_string(); - if (!text.empty()) - { - auto undo_ptr = std::unique_ptr(new undo_move_text(*this)); - undo_ptr->set_selected_text(); - - //Determines whether the caret is at left or at right. The select_.b indicates the caret position when finish selection - const bool at_left = (select_.b < select_.a); + std::wstring text; nana::upoint a, b; - get_selected_points(a, b); - if (caret.y < a.y || (caret.y == a.y && caret.x < a.x)) - {//forward - undo_ptr->set_caret_pos(); - - _m_erase_select(false); - _m_put(text, false); - - select_.a = caret; - select_.b.y = b.y + (caret.y - a.y); - } - else if (b.y < caret.y || (caret.y == b.y && b.x < caret.x)) + if (get_selected_points(a, b)) { - undo_ptr->set_caret_pos(); + auto & textbase = this->textbase(); - _m_put(text, false); - _m_erase_select(false); - - select_.b.y = caret.y; - select_.a.y = caret.y - (b.y - a.y); - select_.a.x = caret.x - (caret.y == b.y ? (b.x - a.x) : 0); + if (a.y != b.y) + { + text = textbase.getline(a.y).substr(a.x); + text += L"\r\n"; + for (unsigned i = a.y + 1; i < b.y; ++i) + { + text += textbase.getline(i); + text += L"\r\n"; + } + text += textbase.getline(b.y).substr(0, b.x); + } + else + return textbase.getline(a.y).substr(a.x, b.x - a.x); } - select_.b.x = b.x + (a.y == b.y ? (select_.a.x - a.x) : 0); - //restores the caret at the proper end. - if ((select_.b < select_.a) != at_left) - std::swap(select_.a, select_.b); + return text; + } - if (record_undo) + std::size_t eat_endl(const wchar_t* str, std::size_t pos) + { + auto ch = str[pos]; + if (0 == ch) + return pos; + + const wchar_t * endlstr; + switch (ch) { - undo_ptr->set_destination(select_.a, select_.b); - impl_->undo.push(std::move(undo_ptr)); + case L'\n': + endlstr = L"\n\r"; + break; + case L'\r': + endlstr = L"\r\n"; + break; + default: + return pos; } - points_.caret = select_.b; - reset_caret(); + if (std::memcmp(str + pos, endlstr, sizeof(wchar_t) * 2) == 0) + return pos + 2; + + return pos + 1; + } + + bool text_editor::_m_resolve_text(const std::wstring& text, std::vector> & lines) + { + auto const text_str = text.c_str(); + std::size_t begin = 0; + while (true) + { + auto pos = text.find_first_of(L"\r\n", begin); + if (text.npos == pos) + { + if (!lines.empty()) + lines.emplace_back(begin, text.size()); + break; + } + + lines.emplace_back(begin, pos); + + pos = eat_endl(text_str, pos); + + begin = text.find_first_not_of(L"\r\n", pos); + + //The number of new lines minus one + const auto chp_end = text_str + (begin == text.npos ? text.size() : begin); + + for (auto chp = text_str + pos; chp != chp_end; ++chp) + { + auto eats = eat_endl(chp, 0); + if (eats) + { + lines.emplace_back(); + chp += (eats - 1); + } + } + + if (text.npos == begin) + { + lines.emplace_back(); + break; + } + } + return !lines.empty(); + } + + bool text_editor::_m_cancel_select(int align) + { + upoint a, b; + if (get_selected_points(a, b)) + { + //domain of algin = [0, 2] + if (align) + { + this->points_.caret = (1 == align ? a : b); + this->_m_adjust_view(); + } + + select_.a = select_.b = points_.caret; + reset_caret(); + return true; + } + return false; + } + + nana::size text_editor::_m_text_extent_size(const char_type* str, size_type n) const + { + if (mask_char_) + { + std::wstring maskstr; + maskstr.append(n, mask_char_); + return graph_.text_extent_size(maskstr); + } +#ifdef _nana_std_has_string_view + return graph_.text_extent_size(std::basic_string_view(str, n)); +#else + return graph_.text_extent_size(str, static_cast(n)); +#endif + } + + bool text_editor::_m_adjust_view() + { + auto const view_area = impl_->cview->view_area(); + + auto const line_px = static_cast(this->line_height()); + auto coord = _m_caret_to_coordinate(points_.caret, true); + + if (view_area.is_hit(coord) && view_area.is_hit({ coord.x, coord.y + line_px })) + return false; + + unsigned extra_count_horz = 4; + unsigned extra_count_vert = 0; + + auto const origin = impl_->cview->origin(); + coord = _m_caret_to_coordinate(points_.caret, false); + + point moved_origin; + + //adjust x-axis if it isn't line-wrapped mode + if (!attributes_.line_wrapped) + { + auto extra = points_.caret; + + if (coord.x < origin.x) + { + extra.x -= (std::min)(extra_count_horz, points_.caret.x); + moved_origin.x = _m_caret_to_coordinate(extra, false).x - origin.x; + } + else if (coord.x + static_cast(caret_size().width) >= origin.x + static_cast(view_area.width)) + { + extra.x = (std::min)(static_cast(textbase().getline(points_.caret.y).size()), points_.caret.x + extra_count_horz); + auto new_origin = _m_caret_to_coordinate(extra, false).x + static_cast(caret_size().width) - static_cast(view_area.width); + moved_origin.x = new_origin - origin.x; + } + } + + auto extra_px = static_cast(line_px * extra_count_vert); + + if (coord.y < origin.y) + { + //Top of caret is less than the top of view + + moved_origin.y = (std::max)(0, coord.y - extra_px) - origin.y; + } + else if (coord.y + line_px >= origin.y + static_cast(view_area.height)) + { + //Bottom of caret is greater than the bottom of view + + auto const bottom = static_cast(impl_->capacities.behavior->take_lines() * line_px); + auto new_origin = (std::min)(coord.y + line_px + extra_px, bottom) - static_cast(view_area.height); + moved_origin.y = new_origin - origin.y; + } + + return impl_->cview->move_origin(moved_origin); + } + + bool text_editor::_m_move_select(bool record_undo) + { + if (!attributes_.editable) + return false; + + nana::upoint caret = points_.caret; + const auto text = _m_make_select_string(); + if (!text.empty()) + { + auto undo_ptr = std::unique_ptr(new undo_move_text(*this)); + undo_ptr->set_selected_text(); + + //Determines whether the caret is at left or at right. The select_.b indicates the caret position when finish selection + const bool at_left = (select_.b < select_.a); + + nana::upoint a, b; + get_selected_points(a, b); + if (caret.y < a.y || (caret.y == a.y && caret.x < a.x)) + {//forward + undo_ptr->set_caret_pos(); + + _m_erase_select(false); + _m_put(text, false); + + select_.a = caret; + select_.b.y = b.y + (caret.y - a.y); + } + else if (b.y < caret.y || (caret.y == b.y && b.x < caret.x)) + { + undo_ptr->set_caret_pos(); + + _m_put(text, false); + _m_erase_select(false); + + select_.b.y = caret.y; + select_.a.y = caret.y - (b.y - a.y); + select_.a.x = caret.x - (caret.y == b.y ? (b.x - a.x) : 0); + } + select_.b.x = b.x + (a.y == b.y ? (select_.a.x - a.x) : 0); + + //restores the caret at the proper end. + if ((select_.b < select_.a) != at_left) + std::swap(select_.a, select_.b); + + if (record_undo) + { + undo_ptr->set_destination(select_.a, select_.b); + impl_->undo.push(std::move(undo_ptr)); + } + + points_.caret = select_.b; + reset_caret(); + return true; + } + return false; + } + + int text_editor::_m_text_top_base() const + { + if (false == attributes_.multi_lines) + { + unsigned px = line_height(); + if (text_area_.area.height > px) + return text_area_.area.y + static_cast((text_area_.area.height - px) >> 1); + } + return text_area_.area.y; + } + + int text_editor::_m_text_topline() const + { + auto px = static_cast(line_height()); + return (px ? (impl_->cview->origin().y / px) : px); + } + + int text_editor::_m_text_x(const text_section& sct) const + { + auto const behavior = impl_->capacities.behavior; + int left = this->text_area_.area.x; + + if (::nana::align::left != this->attributes_.alignment) + { + auto blank_px = behavior->max_pixels() - sct.pixels; + + if (::nana::align::center == this->attributes_.alignment) + left += static_cast(blank_px) / 2; + else + left += static_cast(blank_px); + } + + return left - impl_->cview->origin().x; + } + + + void text_editor::_m_draw_parse_string(const keyword_parser& parser, bool rtl, ::nana::point pos, const ::nana::color& fgcolor, const wchar_t* str, std::size_t len) const + { +#ifdef _nana_std_has_string_view + graph_.string(pos, { str, len }, fgcolor); +#else + graph_.palette(true, fgcolor); + graph_.string(pos, str, len); +#endif + if (parser.entities().empty()) + return; + +#ifdef _nana_std_has_string_view + auto glyph_px = graph_.glyph_pixels({ str, len }); +#else + std::unique_ptr glyph_px(new unsigned[len]); + graph_.glyph_pixels(str, len, glyph_px.get()); +#endif + auto glyphs = glyph_px.get(); + + auto px_h = line_height(); + auto px_w = std::accumulate(glyphs, glyphs + len, unsigned{}); + + ::nana::paint::graphics canvas; + canvas.make({ px_w, px_h }); + canvas.typeface(graph_.typeface()); + ::nana::point canvas_text_pos; + + auto ent_pos = pos; + const auto str_end = str + len; + auto & entities = parser.entities(); + + for (auto & ent : entities) + { + const wchar_t* ent_begin = nullptr; + + int ent_off = 0; + if (str <= ent.begin && ent.begin < str_end) + { + ent_begin = ent.begin; + ent_off = static_cast(std::accumulate(glyphs, glyphs + (ent.begin - str), unsigned{})); + } + else if (ent.begin <= str && str < ent.end) + ent_begin = str; + + if (ent_begin) + { + auto ent_end = (ent.end < str_end ? ent.end : str_end); + auto ent_pixels = std::accumulate(glyphs + (ent_begin - str), glyphs + (ent_end - str), unsigned{}); + + canvas.palette(false, ent.scheme->bgcolor.invisible() ? _m_bgcolor() : ent.scheme->bgcolor); + canvas.palette(true, ent.scheme->fgcolor.invisible() ? fgcolor : ent.scheme->fgcolor); + + canvas.rectangle(true); + + ent_pos.x = pos.x + ent_off; + +#ifdef _nana_std_has_string_view + std::wstring_view ent_sv; + if (rtl) + { + //draw the whole text if it is a RTL text, because Arbic language is transformable. + ent_sv = { str, len }; + } + else + { + ent_sv = { ent_begin, static_cast(ent_end - ent_begin) }; + ent_off = 0; + } + canvas.string({}, ent_sv); +#else + if (rtl) + { + //draw the whole text if it is a RTL text, because Arbic language is transformable. + canvas.string({}, str, len); + } + else + { + canvas.string({}, ent_begin, ent_end - ent_begin); + ent_off = 0; + } +#endif + graph_.bitblt(rectangle{ ent_pos, size{ ent_pixels, canvas.height() } }, canvas, point{ ent_off, 0 }); + } + } + } + + class text_editor::helper_pencil + { + public: + helper_pencil(paint::graphics& graph, const text_editor& editor, keyword_parser& parser) : + graph_(graph), + editor_(editor), + parser_(parser), + line_px_(editor.line_height()) + {} + + color selection_color(bool fgcolor, bool focused) const + { + if (fgcolor) + return (focused ? editor_.scheme_->selection_text : editor_.scheme_->foreground).get_color(); + + return (focused ? editor_.scheme_->selection : editor_.scheme_->selection_unfocused).get_color(); + } + + void write_selection(const point& text_pos, unsigned text_px, const wchar_t* text, std::size_t len, bool has_focused) + { +#ifdef _nana_std_has_string_view + graph_.rectangle(::nana::rectangle{ text_pos,{ text_px, line_px_ } }, true, + selection_color(false, has_focused)); + + graph_.string(text_pos, { text, len }, selection_color(true, has_focused)); +#else + graph_.palette(true, selection_color(true, has_focused)); + + graph_.rectangle(::nana::rectangle{ text_pos,{ text_px, line_px_ } }, true, + selection_color(false, has_focused)); + + graph_.string(text_pos, text, len); +#endif + } + + void rtl_string(point strpos, const wchar_t* str, std::size_t len, std::size_t str_px, unsigned glyph_front, unsigned glyph_selected, bool has_focused) + { + editor_._m_draw_parse_string(parser_, true, strpos, selection_color(true, has_focused), str, len); + + //Draw selected part + paint::graphics graph({ glyph_selected, line_px_ }); + graph.typeface(this->graph_.typeface()); + graph.rectangle(true, selection_color(false, has_focused)); + + int sel_xpos = static_cast(str_px - (glyph_front + glyph_selected)); + +#ifdef _nana_std_has_string_view + graph.string({ -sel_xpos, 0 }, { str, len }, selection_color(true, has_focused)); +#else + graph.palette(true, selection_color(true, has_focused)); + graph.string({ -sel_xpos, 0 }, str, len); +#endif + graph_.bitblt(nana::rectangle(strpos.x + sel_xpos, strpos.y, glyph_selected, line_px_), graph); + }; + private: + paint::graphics& graph_; + const text_editor& editor_; + keyword_parser & parser_; + unsigned line_px_; + }; + + void text_editor::_m_draw_string(int top, const ::nana::color& clr, const nana::upoint& text_coord, const text_section& sct, bool if_mask) const + { + point text_draw_pos{ _m_text_x(sct), top }; + + const int text_right = text_area_.area.right(); + auto const text_len = static_cast(sct.end - sct.begin); + auto text_ptr = sct.begin; + + std::wstring mask_str; + if (if_mask && mask_char_) + { + mask_str.resize(text_len, mask_char_); + text_ptr = mask_str.c_str(); + } + + const auto focused = API::is_focus_ready(window_); + + auto const reordered = unicode_reorder(text_ptr, text_len); + + //Parse highlight keywords + keyword_parser parser; + parser.parse(text_ptr, text_len, impl_->keywords); + + const auto line_h_pixels = line_height(); + + helper_pencil pencil(graph_, *this, parser); + + graph_.palette(true, clr); + graph_.palette(false, scheme_->selection.get_color()); + + + //Get the selection begin and end position of the current text. + const wchar_t *sbegin = nullptr, *send = nullptr; + + nana::upoint a, b; + if (get_selected_points(a, b)) + { + if (a.y < text_coord.y && text_coord.y < b.y) + { + sbegin = sct.begin; + send = sct.end; + } + else if ((a.y == b.y) && a.y == text_coord.y) + { + auto sbegin_pos = (std::max)(a.x, text_coord.x); + auto send_pos = (std::min)(text_coord.x + text_len, b.x); + + if (sbegin_pos < send_pos) + { + sbegin = text_ptr + (sbegin_pos - text_coord.x); + send = text_ptr + (send_pos - text_coord.x); + } + } + else if (a.y == text_coord.y) + { + if (a.x < text_coord.x + text_len) + { + sbegin = text_ptr; + if (text_coord.x < a.x) + sbegin += (a.x - text_coord.x); + + send = text_ptr + text_len; + } + } + else if (b.y == text_coord.y) + { + if (text_coord.x < b.x) + { + sbegin = text_ptr; + send = text_ptr + (std::min)(b.x - text_coord.x, text_len); + } + } + } + + //A text editor feature, it draws an extra block at end of line if the end of line is in range of selection. + bool extra_space = false; + + //Create a flag for indicating whether the whole line is selected + const bool text_selected = (sbegin == text_ptr && send == text_ptr + text_len); + + //The text is not selected or the whole line text is selected + if ((!sbegin || !send) || text_selected) + { + for (auto & ent : reordered) + { + std::size_t len = ent.end - ent.begin; + +#ifdef _nana_std_has_string_view + unsigned str_w = graph_.text_extent_size(std::wstring_view{ ent.begin, len }).width; +#else + unsigned str_w = graph_.text_extent_size(ent.begin, len).width; +#endif + + if ((text_draw_pos.x + static_cast(str_w) > text_area_.area.x) && (text_draw_pos.x < text_right)) + { + if (text_selected) + pencil.write_selection(text_draw_pos, str_w, ent.begin, len, focused); + else + _m_draw_parse_string(parser, is_right_text(ent), text_draw_pos, clr, ent.begin, len); + } + text_draw_pos.x += static_cast(str_w); + } + + extra_space = text_selected; + } + else + { + for (auto & ent : reordered) + { + const auto len = ent.end - ent.begin; +#ifdef _nana_std_has_string_view + auto ent_px = graph_.text_extent_size(std::wstring_view(ent.begin, len)).width; +#else + auto ent_px = graph_.text_extent_size(ent.begin, len).width; +#endif + + extra_space = false; + + //Only draw the text which is in the visual rectangle. + if ((text_draw_pos.x + static_cast(ent_px) > text_area_.area.x) && (text_draw_pos.x < text_right)) + { + if (send <= ent.begin || ent.end <= sbegin) + { + //this string is not selected + _m_draw_parse_string(parser, false, text_draw_pos, clr, ent.begin, len); + } + else if (sbegin <= ent.begin && ent.end <= send) + { + //this string is completed selected + pencil.write_selection(text_draw_pos, ent_px, ent.begin, len, focused); + extra_space = true; + } + else + { + //a part of string is selected + + //get the selected range of this string. + auto ent_sbegin = (std::max)(sbegin, ent.begin); + auto ent_send = (std::min)(send, ent.end); + + unsigned select_pos = static_cast(ent_sbegin != ent.begin ? ent_sbegin - ent.begin : 0); + unsigned select_len = static_cast(ent_send - ent_sbegin); + +#ifdef _nana_std_has_string_view + auto pxbuf = graph_.glyph_pixels({ ent.begin, static_cast(len) }); +#else + std::unique_ptr pxbuf{ new unsigned[len] }; + graph_.glyph_pixels(ent.begin, len, pxbuf.get()); +#endif + + auto head_px = std::accumulate(pxbuf.get(), pxbuf.get() + select_pos, unsigned{}); + auto select_px = std::accumulate(pxbuf.get() + select_pos, pxbuf.get() + select_pos + select_len, unsigned{}); + + graph_.palette(true, clr); + if (is_right_text(ent)) + { //RTL + pencil.rtl_string(text_draw_pos, ent.begin, len, ent_px, head_px, select_px, focused); + } + else + { //LTR + _m_draw_parse_string(parser, false, text_draw_pos, clr, ent.begin, select_pos); + + auto part_pos = text_draw_pos; + part_pos.x += static_cast(head_px); + + pencil.write_selection(part_pos, select_px, ent.begin + select_pos, select_len, focused); + + if (ent_send < ent.end) + { + part_pos.x += static_cast(select_px); + _m_draw_parse_string(parser, false, part_pos, clr, ent_send, ent.end - ent_send); + } + } + + extra_space = (select_pos + select_len == text_len); + } + } + text_draw_pos.x += static_cast(ent_px); + }//end for + } + + //extra_space is true if the end of line is selected + if (extra_space) + { + //draw the extra space if end of line is not equal to the second selection position. + auto pos = text_coord.x + text_len; + if (b.x != pos || text_coord.y != b.y) + { +#ifdef _nana_std_has_string_view + auto whitespace_w = graph_.text_extent_size(std::wstring_view{ L" ", 1 }).width; +#else + auto whitespace_w = graph_.text_extent_size(L" ", 1).width; +#endif + graph_.rectangle(::nana::rectangle{ text_draw_pos,{ whitespace_w, line_h_pixels } }, true); + } + } + } + + bool text_editor::_m_update_caret_line(std::size_t secondary_before) + { + if (false == this->_m_adjust_view()) + { + if (_m_caret_to_coordinate(points_.caret).x < impl_->cview->view_area().right()) + { + _m_update_line(points_.caret.y, secondary_before); + return false; + } + } + else + { + //The content view is adjusted, now syncs it with active mode to avoid updating. + impl_->cview->sync(false); + } + impl_->try_refresh = sync_graph::refresh; return true; } - return false; - } - int text_editor::_m_text_top_base() const - { - if(false == attributes_.multi_lines) + unsigned text_editor::_m_char_by_pixels(const unicode_bidi::entity& ent, unsigned pos) const { - unsigned px = line_height(); - if(text_area_.area.height > px) - return text_area_.area.y + static_cast((text_area_.area.height - px) >> 1); - } - return text_area_.area.y; - } - - int text_editor::_m_text_topline() const - { - auto px = static_cast(line_height()); - return (px ? (impl_->cview->origin().y / px) : px); - } - - int text_editor::_m_text_x(const text_section& sct) const - { - auto const behavior = impl_->capacities.behavior; - int left = this->text_area_.area.x; - - if (::nana::align::left != this->attributes_.alignment) - { - auto blank_px = behavior->max_pixels() - sct.pixels; - - if (::nana::align::center == this->attributes_.alignment) - left += static_cast(blank_px) / 2; - else - left += static_cast(blank_px); - } - - return left - impl_->cview->origin().x; - } - - - void text_editor::_m_draw_parse_string(const keyword_parser& parser, bool rtl, ::nana::point pos, const ::nana::color& fgcolor, const wchar_t* str, std::size_t len) const - { + auto len = static_cast(ent.end - ent.begin); #ifdef _nana_std_has_string_view - graph_.string(pos, { str, len }, fgcolor); + auto pxbuf = graph_.glyph_pixels({ ent.begin, len }); + if (pxbuf) #else - graph_.palette(true, fgcolor); - graph_.string(pos, str, len); + std::unique_ptr pxbuf(new unsigned[len]); + if (graph_.glyph_pixels(ent.begin, len, pxbuf.get())) #endif - if (parser.entities().empty()) - return; - -#ifdef _nana_std_has_string_view - auto glyph_px = graph_.glyph_pixels({ str, len }); -#else - std::unique_ptr glyph_px(new unsigned[len]); - graph_.glyph_pixels(str, len, glyph_px.get()); -#endif - auto glyphs = glyph_px.get(); - - auto px_h = line_height(); - auto px_w = std::accumulate(glyphs, glyphs + len, unsigned{}); - - ::nana::paint::graphics canvas; - canvas.make({ px_w, px_h }); - canvas.typeface(graph_.typeface()); - ::nana::point canvas_text_pos; - - auto ent_pos = pos; - const auto str_end = str + len; - auto & entities = parser.entities(); - - for (auto & ent : entities) - { - const wchar_t* ent_begin = nullptr; - - int ent_off = 0; - if (str <= ent.begin && ent.begin < str_end) { - ent_begin = ent.begin; - ent_off = static_cast(std::accumulate(glyphs, glyphs + (ent.begin - str), unsigned{})); - } - else if (ent.begin <= str && str < ent.end) - ent_begin = str; + const auto px_end = pxbuf.get() + len; - if (ent_begin) - { - auto ent_end = (ent.end < str_end ? ent.end : str_end); - auto ent_pixels = std::accumulate(glyphs + (ent_begin - str), glyphs + (ent_end - str), unsigned{}); - - canvas.palette(false, ent.scheme->bgcolor.invisible() ? _m_bgcolor() : ent.scheme->bgcolor); - canvas.palette(true, ent.scheme->fgcolor.invisible() ? fgcolor : ent.scheme->fgcolor); - - canvas.rectangle(true); - - ent_pos.x = pos.x + ent_off; - -#ifdef _nana_std_has_string_view - std::wstring_view ent_sv; - if (rtl) + if (is_right_text(ent)) { - //draw the whole text if it is a RTL text, because Arbic language is transformable. - ent_sv = { str, len }; + auto total_px = std::accumulate(pxbuf.get(), px_end, unsigned{}); + + for (auto p = pxbuf.get(); p != px_end; ++p) + { + auto chpos = total_px - *p; + if ((chpos <= pos) && (pos < total_px)) + { + if ((*p < 2) || (pos <= chpos + (*p >> 1))) + return static_cast(p - pxbuf.get()) + 1; + + return static_cast(p - pxbuf.get()); + } + total_px = chpos; + } } else { - ent_sv = { ent_begin, static_cast(ent_end - ent_begin) }; - ent_off = 0; - } - canvas.string({}, ent_sv); -#else - if (rtl) - { - //draw the whole text if it is a RTL text, because Arbic language is transformable. - canvas.string({}, str, len); - } - else - { - canvas.string({}, ent_begin, ent_end - ent_begin); - ent_off = 0; - } -#endif - graph_.bitblt(rectangle{ ent_pos, size{ ent_pixels, canvas.height() } }, canvas, point{ ent_off, 0 }); - } - } - } - - class text_editor::helper_pencil - { - public: - helper_pencil(paint::graphics& graph, const text_editor& editor, keyword_parser& parser): - graph_( graph ), - editor_( editor ), - parser_( parser ), - line_px_( editor.line_height() ) - {} - - color selection_color(bool fgcolor, bool focused) const - { - if (fgcolor) - return (focused ? editor_.scheme_->selection_text : editor_.scheme_->foreground).get_color(); - - return (focused ? editor_.scheme_->selection : editor_.scheme_->selection_unfocused).get_color(); - } - - void write_selection(const point& text_pos, unsigned text_px, const wchar_t* text, std::size_t len, bool has_focused) - { -#ifdef _nana_std_has_string_view - graph_.rectangle(::nana::rectangle{ text_pos,{ text_px, line_px_ } }, true, - selection_color(false, has_focused)); - - graph_.string(text_pos, { text, len }, selection_color(true, has_focused)); -#else - graph_.palette(true, selection_color(true, has_focused)); - - graph_.rectangle(::nana::rectangle{ text_pos, { text_px, line_px_ } }, true, - selection_color(false, has_focused)); - - graph_.string(text_pos, text, len); -#endif - } - - void rtl_string(point strpos, const wchar_t* str, std::size_t len, std::size_t str_px, unsigned glyph_front, unsigned glyph_selected, bool has_focused) - { - editor_._m_draw_parse_string(parser_, true, strpos, selection_color(true, has_focused), str, len); - - //Draw selected part - paint::graphics graph({ glyph_selected, line_px_ }); - graph.typeface(this->graph_.typeface()); - graph.rectangle(true, selection_color(false, has_focused)); - - int sel_xpos = static_cast(str_px - (glyph_front + glyph_selected)); - -#ifdef _nana_std_has_string_view - graph.string({ -sel_xpos, 0 }, { str, len }, selection_color(true, has_focused)); -#else - graph.palette(true, selection_color(true, has_focused)); - graph.string({ -sel_xpos, 0 }, str, len); -#endif - graph_.bitblt(nana::rectangle(strpos.x + sel_xpos, strpos.y, glyph_selected, line_px_), graph); - }; - private: - paint::graphics& graph_; - const text_editor& editor_; - keyword_parser & parser_; - unsigned line_px_; - }; - - void text_editor::_m_draw_string(int top, const ::nana::color& clr, const nana::upoint& text_coord, const text_section& sct, bool if_mask) const - { - point text_draw_pos{ _m_text_x(sct), top }; - - const int text_right = text_area_.area.right(); - auto const text_len = static_cast(sct.end - sct.begin); - auto text_ptr = sct.begin; - - std::wstring mask_str; - if (if_mask && mask_char_) - { - mask_str.resize(text_len, mask_char_); - text_ptr = mask_str.c_str(); - } - - const auto focused = API::is_focus_ready(window_); - - auto const reordered = unicode_reorder(text_ptr, text_len); - - //Parse highlight keywords - keyword_parser parser; - parser.parse(text_ptr, text_len, impl_->keywords); - - const auto line_h_pixels = line_height(); - - helper_pencil pencil(graph_, *this, parser); - - graph_.palette(true, clr); - graph_.palette(false, scheme_->selection.get_color()); - - - //Get the selection begin and end position of the current text. - const wchar_t *sbegin = nullptr, *send = nullptr; - - nana::upoint a, b; - if (get_selected_points(a, b)) - { - if (a.y < text_coord.y && text_coord.y < b.y) - { - sbegin = sct.begin; - send = sct.end; - } - else if ((a.y == b.y) && a.y == text_coord.y) - { - auto sbegin_pos = (std::max)(a.x, text_coord.x); - auto send_pos = (std::min)(text_coord.x + text_len, b.x); - - if (sbegin_pos < send_pos) - { - sbegin = text_ptr + (sbegin_pos - text_coord.x); - send = text_ptr + (send_pos - text_coord.x); + for (auto p = pxbuf.get(); p != px_end; ++p) + { + if (pos <= *p) + { + if ((*p > 1) && (pos >(*p >> 1))) + return static_cast(p - pxbuf.get()) + 1; + return static_cast(p - pxbuf.get()); + } + pos -= *p; + } } } - else if (a.y == text_coord.y) - { - if (a.x < text_coord.x + text_len) - { - sbegin = text_ptr; - if (text_coord.x < a.x) - sbegin += (a.x - text_coord.x); - send = text_ptr + text_len; - } - } - else if (b.y == text_coord.y) - { - if (text_coord.x < b.x) - { - sbegin = text_ptr; - send = text_ptr + (std::min)(b.x - text_coord.x, text_len); - } - } + return 0; } - //A text editor feature, it draws an extra block at end of line if the end of line is in range of selection. - bool extra_space = false; - - //Create a flag for indicating whether the whole line is selected - const bool text_selected = (sbegin == text_ptr && send == text_ptr+ text_len); - - //The text is not selected or the whole line text is selected - if ((!sbegin || !send) || text_selected) + unsigned text_editor::_m_pixels_by_char(const std::wstring& lnstr, std::size_t pos) const { + if (pos > lnstr.size()) + return 0; + + auto const reordered = unicode_reorder(lnstr.c_str(), lnstr.size()); + + auto target = lnstr.c_str() + pos; + + unsigned text_w = 0; for (auto & ent : reordered) { std::size_t len = ent.end - ent.begin; - -#ifdef _nana_std_has_string_view - unsigned str_w = graph_.text_extent_size(std::wstring_view{ ent.begin, len }).width; -#else - unsigned str_w = graph_.text_extent_size(ent.begin, len).width; -#endif - - if ((text_draw_pos.x + static_cast(str_w) > text_area_.area.x) && (text_draw_pos.x < text_right)) + if (ent.begin <= target && target <= ent.end) { - if (text_selected) - pencil.write_selection(text_draw_pos, str_w, ent.begin, len, focused); - else - _m_draw_parse_string(parser, is_right_text(ent), text_draw_pos, clr, ent.begin, len); - } - text_draw_pos.x += static_cast(str_w); - } - - extra_space = text_selected; - } - else - { - for (auto & ent : reordered) - { - const auto len = ent.end - ent.begin; + if (is_right_text(ent)) + { + //Characters of some bidi languages may transform in a word. + //RTL #ifdef _nana_std_has_string_view - auto ent_px = graph_.text_extent_size(std::wstring_view(ent.begin, len)).width; + auto pxbuf = graph_.glyph_pixels({ ent.begin, len }); #else - auto ent_px = graph_.text_extent_size(ent.begin, len).width; -#endif - - extra_space = false; - - //Only draw the text which is in the visual rectangle. - if ((text_draw_pos.x + static_cast(ent_px) > text_area_.area.x) && (text_draw_pos.x < text_right)) - { - if (send <= ent.begin || ent.end <= sbegin) - { - //this string is not selected - _m_draw_parse_string(parser, false, text_draw_pos, clr, ent.begin, len); - } - else if (sbegin <= ent.begin && ent.end <= send) - { - //this string is completed selected - pencil.write_selection(text_draw_pos, ent_px, ent.begin, len, focused); - extra_space = true; - } - else - { - //a part of string is selected - - //get the selected range of this string. - auto ent_sbegin = (std::max)(sbegin, ent.begin); - auto ent_send = (std::min)(send, ent.end); - - unsigned select_pos = static_cast(ent_sbegin != ent.begin ? ent_sbegin - ent.begin : 0); - unsigned select_len = static_cast(ent_send - ent_sbegin); - -#ifdef _nana_std_has_string_view - auto pxbuf = graph_.glyph_pixels({ ent.begin, static_cast(len) }); -#else - std::unique_ptr pxbuf{ new unsigned[len] }; + std::unique_ptr pxbuf(new unsigned[len]); graph_.glyph_pixels(ent.begin, len, pxbuf.get()); #endif - - auto head_px = std::accumulate(pxbuf.get(), pxbuf.get() + select_pos, unsigned{}); - auto select_px = std::accumulate(pxbuf.get() + select_pos, pxbuf.get() + select_pos + select_len, unsigned{}); - - graph_.palette(true, clr); - if (is_right_text(ent)) - { //RTL - pencil.rtl_string(text_draw_pos, ent.begin, len, ent_px, head_px, select_px, focused); - } - else - { //LTR - _m_draw_parse_string(parser, false, text_draw_pos, clr, ent.begin, select_pos); - - auto part_pos = text_draw_pos; - part_pos.x += static_cast(head_px); - - pencil.write_selection(part_pos, select_px, ent.begin + select_pos, select_len, focused); - - if (ent_send < ent.end) - { - part_pos.x += static_cast(select_px); - _m_draw_parse_string(parser, false, part_pos, clr, ent_send, ent.end - ent_send); - } - } - - extra_space = (select_pos + select_len == text_len); + return std::accumulate(pxbuf.get() + (target - ent.begin), pxbuf.get() + len, text_w); } + //LTR + return text_w + _m_text_extent_size(ent.begin, target - ent.begin).width; } - text_draw_pos.x += static_cast(ent_px); - }//end for - } - - //extra_space is true if the end of line is selected - if (extra_space) - { - //draw the extra space if end of line is not equal to the second selection position. - auto pos = text_coord.x + text_len; - if (b.x != pos || text_coord.y != b.y) - { -#ifdef _nana_std_has_string_view - auto whitespace_w = graph_.text_extent_size(std::wstring_view{ L" ", 1 }).width; -#else - auto whitespace_w = graph_.text_extent_size(L" ", 1).width; -#endif - graph_.rectangle(::nana::rectangle{ text_draw_pos, { whitespace_w, line_h_pixels } }, true); - } - } - } - - bool text_editor::_m_update_caret_line(std::size_t secondary_before) - { - if (false == this->_m_adjust_view()) - { - if (_m_caret_to_coordinate(points_.caret).x < impl_->cview->view_area().right()) - { - _m_update_line(points_.caret.y, secondary_before); - return false; - } - } - else - { - //The content view is adjusted, now syncs it with active mode to avoid updating. - impl_->cview->sync(false); - } - impl_->try_refresh = sync_graph::refresh; - return true; - } - - unsigned text_editor::_m_char_by_pixels(const unicode_bidi::entity& ent, unsigned pos) const - { - auto len = static_cast(ent.end - ent.begin); -#ifdef _nana_std_has_string_view - auto pxbuf = graph_.glyph_pixels({ ent.begin, len }); - if (pxbuf) -#else - std::unique_ptr pxbuf(new unsigned[len]); - if (graph_.glyph_pixels(ent.begin, len, pxbuf.get())) -#endif - { - const auto px_end = pxbuf.get() + len; - - if (is_right_text(ent)) - { - auto total_px = std::accumulate(pxbuf.get(), px_end, unsigned{}); - - for (auto p = pxbuf.get(); p != px_end; ++p) - { - auto chpos = total_px - *p; - if ((chpos <= pos) && (pos < total_px)) - { - if ((*p < 2) || (pos <= chpos + (*p >> 1))) - return static_cast(p - pxbuf.get()) + 1; - - return static_cast(p - pxbuf.get()); - } - total_px = chpos; - } - } - else - { - for (auto p = pxbuf.get(); p != px_end; ++p) - { - if (pos <= *p) - { - if ((*p > 1) && (pos >(*p >> 1))) - return static_cast(p - pxbuf.get()) + 1; - return static_cast(p - pxbuf.get()); - } - pos -= *p; - } + else + text_w += _m_text_extent_size(ent.begin, len).width; } + return text_w; } - return 0; - } - - unsigned text_editor::_m_pixels_by_char(const std::wstring& lnstr, std::size_t pos) const - { - if (pos > lnstr.size()) - return 0; - - auto const reordered = unicode_reorder(lnstr.c_str(), lnstr.size()); - - auto target = lnstr.c_str() + pos; - - unsigned text_w = 0; - for (auto & ent : reordered) - { - std::size_t len = ent.end - ent.begin; - if (ent.begin <= target && target <= ent.end) - { - if (is_right_text(ent)) - { - //Characters of some bidi languages may transform in a word. - //RTL -#ifdef _nana_std_has_string_view - auto pxbuf = graph_.glyph_pixels({ent.begin, len}); -#else - std::unique_ptr pxbuf(new unsigned[len]); - graph_.glyph_pixels(ent.begin, len, pxbuf.get()); -#endif - return std::accumulate(pxbuf.get() + (target - ent.begin), pxbuf.get() + len, text_w); - } - //LTR - return text_w + _m_text_extent_size(ent.begin, target - ent.begin).width; - } - else - text_w += _m_text_extent_size(ent.begin, len).width; - } - return text_w; - } - - //end class text_editor - }//end namespace skeletons -}//end namespace widgets + //end class text_editor + }//end namespace skeletons + }//end namespace widgets }//end namespace nana From b87d17cac1daf1f5e6caa69892eec0b4605e702f Mon Sep 17 00:00:00 2001 From: Jinhao Date: Sun, 4 Nov 2018 13:50:12 +0800 Subject: [PATCH 073/149] bug fix and improvement of listbox --- source/gui/widgets/listbox.cpp | 157 ++++++++++----------------------- 1 file changed, 46 insertions(+), 111 deletions(-) diff --git a/source/gui/widgets/listbox.cpp b/source/gui/widgets/listbox.cpp index 6c4c38c0..e2316f4e 100644 --- a/source/gui/widgets/listbox.cpp +++ b/source/gui/widgets/listbox.cpp @@ -1987,9 +1987,6 @@ namespace nana bool auto_draw{true}; bool checkable{false}; bool if_image{false}; -#if 0 //deprecated - bool deselect_deferred{ false }; //deselects items when mouse button is released. -#endif unsigned text_height; ::nana::listbox::export_options def_exp_options; @@ -3544,7 +3541,7 @@ namespace nana :essence_(es) {} - void draw(const nana::rectangle& rect) + void draw(const nana::rectangle& visual_r) { internal_scope_guard lock; @@ -3568,10 +3565,10 @@ namespace nana auto const origin = essence_->content_view->origin(); auto const header_margin = essence_->header.margin(); - if (header_w + header_margin < origin.x + rect.width) + if (header_w + header_margin < origin.x + visual_r.width) { - rectangle r{ point{ rect.x + static_cast(header_w + header_margin) - origin.x, rect.y }, - size{ rect.width + origin.x - header_w, rect.height } }; + rectangle r{ point{ visual_r.x + static_cast(header_w + header_margin) - origin.x, visual_r.y }, + size{ visual_r.width + origin.x - header_w, visual_r.height } }; if (!API::dev::copy_transparent_background(essence_->listbox_ptr->handle(), r, *essence_->graph, r.position())) essence_->graph->rectangle(r, true); @@ -3579,7 +3576,7 @@ namespace nana if (header_margin > 0) { - rectangle r = rect; + rectangle r = visual_r; r.width = header_margin; if (!API::dev::copy_transparent_background(essence_->listbox_ptr->handle(), r, *essence_->graph, r.position())) @@ -3593,8 +3590,8 @@ namespace nana auto first_disp = essence_->first_display(); point item_coord{ - essence_->item_xpos(rect), - rect.y - static_cast(origin.y % item_height_px) + essence_->item_xpos(visual_r), + visual_r.y - static_cast(origin.y % item_height_px) }; essence_->inline_buffered_table.swap(essence_->inline_table); @@ -3610,17 +3607,13 @@ namespace nana hoverred_pos = lister.advance(first_disp, static_cast(ptr_where.second)); } - auto const columns = essence_->ordered_columns(rect.width); + auto const columns = essence_->ordered_columns(visual_r.width); if (columns.empty()) return; auto const txtoff = static_cast(essence_->scheme_ptr->item_height_ex) / 2; - auto i_categ = lister.get(first_disp.cat); - - auto idx = first_disp; - for (auto & cat : lister.cat_container()) for (auto & ind : cat.indicators) { @@ -3628,75 +3621,49 @@ namespace nana ind->detach(); } - //Here we draw the root categ (0) or a first item if the first drawing is not a categ.(item!=npos)) - if (idx.cat == 0 || !idx.is_category()) + auto idx = first_disp; + for (auto i_categ = lister.get(first_disp.cat); i_categ != lister.cat_container().end(); ++i_categ) { - if (idx.cat == 0 && idx.is_category()) // the 0 cat + if (item_coord.y > visual_r.bottom()) + break; + + if (idx.cat > 0 && idx.is_category()) { - first_disp.item = 0; + _m_draw_categ(*i_categ, visual_r.x - origin.x, item_coord.y, txtoff, header_w, bgcolor, + (hoverred_pos.is_category() && (idx.cat == hoverred_pos.cat) ? item_state::highlighted : item_state::normal) + ); + item_coord.y += static_cast(item_height_px); idx.item = 0; } - std::size_t size = i_categ->items.size(); - for (std::size_t offs = first_disp.item; offs < size; ++offs, ++idx.item) + if (i_categ->expand) { - if (item_coord.y >= rect.bottom()) - break; + auto size = i_categ->items.size(); + for (; idx.item < size; ++idx.item) + { + if (item_coord.y > visual_r.bottom()) + break; - auto item_pos = lister.index_cast(index_pair{ idx.cat, offs }, true); //convert display position to absolute position + auto item_pos = lister.index_cast(index_pair{ idx.cat, idx.item }, true); //convert display position to absolute position - _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) - ); + _m_draw_item(*i_categ, item_pos, item_coord, txtoff, header_w, visual_r, columns, bgcolor, fgcolor, + (idx == hoverred_pos ? item_state::highlighted : item_state::normal) + ); - item_coord.y += static_cast(item_height_px); + item_coord.y += static_cast(item_height_px); + } } - ++i_categ; ++idx.cat; - } - - for (; i_categ != lister.cat_container().end(); ++i_categ, ++idx.cat) - { - if (item_coord.y > rect.bottom()) - break; - - idx.item = 0; - - _m_draw_categ(*i_categ, rect.x - origin.x, item_coord.y, txtoff, header_w, bgcolor, - (hoverred_pos.is_category() && (idx.cat == hoverred_pos.cat) ? item_state::highlighted : item_state::normal) - ); - item_coord.y += static_cast(item_height_px); - - if (false == i_categ->expand) - continue; - - auto size = i_categ->items.size(); - for (decltype(size) pos = 0; pos < size; ++pos) - { - if (item_coord.y > rect.bottom()) - break; - - 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, columns, bgcolor, fgcolor, - (idx == hoverred_pos ? item_state::highlighted : item_state::normal) - ); - - item_coord.y += static_cast(item_height_px); - if (item_coord.y >= rect.bottom()) - break; - - ++idx.item; - } + idx.item = nana::npos; } } essence_->inline_buffered_table.clear(); - if (item_coord.y < rect.bottom()) + if (item_coord.y < visual_r.bottom()) { - rectangle bground_r{ rect.x, item_coord.y, rect.width, static_cast(rect.bottom() - item_coord.y) }; + rectangle bground_r{ visual_r.x, item_coord.y, visual_r.width, static_cast(visual_r.bottom() - item_coord.y) }; if (!API::dev::copy_transparent_background(essence_->listbox_ptr->handle(), bground_r, *essence_->graph, bground_r.position())) essence_->graph->rectangle(bground_r, true, bgcolor); } @@ -3820,10 +3787,10 @@ namespace nana void _m_draw_item(const category_t& cat, const index_pair& item_pos, const point& coord, - const int txtoff, ///< below y to print the text - unsigned width, - const nana::rectangle& content_r, ///< the rectangle where the full list content have to be drawn - const std::vector& seqs, ///< columns to print + const int txtoff, ///< below y to print the text + unsigned header_width, ///< width of all visible columns, in pixel + const nana::rectangle& content_r, ///< the rectangle where the full list content have to be drawn + const std::vector& seqs, ///< columns to print nana::color bgcolor, nana::color fgcolor, item_state state @@ -3842,12 +3809,16 @@ namespace nana if(!item.fgcolor.invisible()) fgcolor = item.fgcolor; - const unsigned show_w = (std::min)(content_r.width, width - essence_->content_view->origin().x); + const unsigned columns_shown_width = (std::min)(content_r.width, header_width - essence_->content_view->origin().x); auto graph = essence_->graph; //draw the background for the whole item - rectangle bground_r{ content_r.x + static_cast(essence_->header.margin()), coord.y, show_w, essence_->item_height() }; + rectangle bground_r{ + content_r.x + static_cast(essence_->header.margin()) - essence_->content_view->origin().x, + coord.y, + columns_shown_width + essence_->content_view->origin().x, + essence_->item_height() }; auto const state_bgcolor = this->_m_draw_item_bground(bground_r, bgcolor, {}, state, item); //The position of column in x-axis. @@ -4051,7 +4022,6 @@ namespace nana void _m_draw_item_border(int item_top) const { //Draw selecting inner rectangle - rectangle r{ essence_->content_area().x - essence_->content_view->origin().x + static_cast(essence_->header.margin()), item_top, @@ -4150,13 +4120,8 @@ namespace nana using item_state = essence::item_state; using parts = essence::parts; -#if 0 //deprecated - //Cancel deferred deselection operation when mouse moves. - essence_->deselect_deferred = false; -#else - if (operation_states::msup_deselect == essence_->operation.state) + if ((operation_states::msup_deselect == essence_->operation.state) && API::dev::window_draggable(arg.window_handle)) essence_->operation.state = operation_states::none; -#endif bool need_refresh = false; @@ -4316,20 +4281,6 @@ namespace nana essence_->mouse_selection.reverse_selection = true; new_selected_status = !essence_->cs_status(abs_item_pos, true); } -#if 0 //deprecated - else if (nana::mouse::right_button == arg.button) - { - //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, abs_item_pos); - } - else - { - //Unselects all selected items except current item if right button clicked. - lister.select_for_all(false, abs_item_pos); //cancel all selections - } -#else else { auto selected = lister.pick_items(true); @@ -4343,17 +4294,13 @@ namespace nana { essence_->operation.item = abs_item_pos; - //Don't deselect the selections if the listbox is draggable. - //It should remain the selections for drag-and-drop - - if (!API::dev::window_draggable(arg.window_handle)) - essence_->operation.state = operation_states::msup_deselect; + //Don't deselect the selections, let it determine in mouse_move event depending on whether dnd is enabled. + essence_->operation.state = operation_states::msup_deselect; } } else lister.select_for_all(false, abs_item_pos); } -#endif } else { @@ -4423,15 +4370,11 @@ namespace nana //Deselection of all items is deferred to the mouse up event when ctrl or shift is not pressed //Pressing ctrl or shift is to selects other items without deselecting current selections. -#if 0 //deprecated - essence_->deselect_deferred = !(arg.ctrl || arg.shift); -#else if (!(arg.ctrl || arg.shift)) { essence_->operation.state = operation_states::msup_deselect; essence_->operation.item = index_pair{nana::npos, nana::npos}; } -#endif } if(update) @@ -4473,14 +4416,6 @@ namespace nana need_refresh = true; } -#if 0 //deprecated - if (essence_->deselect_deferred) - { - essence_->deselect_deferred = false; - need_refresh |= (essence_->lister.select_for_all(false)); - } -#endif - if (operation_states::msup_deselect == essence_->operation.state) { essence_->operation.state = operation_states::none; From ee419c88855ed295d4776f5e7bc1c05762da3005 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Mon, 5 Nov 2018 01:33:36 +0800 Subject: [PATCH 074/149] refactoring of listbox --- source/gui/widgets/listbox.cpp | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/source/gui/widgets/listbox.cpp b/source/gui/widgets/listbox.cpp index e2316f4e..0c46cd52 100644 --- a/source/gui/widgets/listbox.cpp +++ b/source/gui/widgets/listbox.cpp @@ -1974,7 +1974,6 @@ namespace nana msup_deselect }; - /// created and live by the trigger, holds data for listbox: the state of the struct does not effect on member funcions, therefore all data members are public. struct essence { @@ -2295,16 +2294,16 @@ namespace nana return header.range(seq.front()).first + r.x - this->content_view->origin().x; } - //Returns the absolute coordinate of the specified item in the window - point item_coordinate(const index_pair& pos) const + //Returns the top of the specified item in listbox window. + int item_window_top(const index_pair& pos) const { - auto top = static_cast(this->lister.distance(index_pair{}, pos) * item_height()) - content_view->origin().y; + int top = static_cast(this->lister.distance(index_pair{}, pos) * item_height()) - content_view->origin().y; rectangle r; if (rect_lister(r)) top += r.y; - return{ top, top + static_cast(item_height()) }; + return top; } std::pair where(const nana::point& pos) const noexcept @@ -4247,13 +4246,21 @@ namespace nana //adjust the display of selected into the list rectangle if the part of the item is beyond the top/bottom edge if (good_list_r) { - auto item_coord = this->essence_->item_coordinate(abs_item_pos); //item_coord.x = top, item_coord.y = bottom - if (item_coord.x < list_r.y && list_r.y < item_coord.y) - essence_->content_view->move_origin({ 0, item_coord.x - list_r.y }); - else if (item_coord.x < list_r.bottom() && list_r.bottom() < item_coord.y) - essence_->content_view->move_origin({ 0, item_coord.y - list_r.bottom() }); + auto const item_top = this->essence_->item_window_top(abs_item_pos); + auto const item_bottom = item_top + static_cast(essence_->item_height()); - essence_->content_view->sync(false); + int move_top = 0; + + if (item_top < list_r.y && list_r.y < item_bottom) + move_top = item_top - list_r.y; + else if (item_top < list_r.bottom() && list_r.bottom() < item_bottom) + move_top = item_bottom - list_r.bottom(); + + if (0 != move_top) + { + essence_->content_view->move_origin({ 0, move_top }); + essence_->content_view->sync(false); + } } bool new_selected_status = true; From b3eacfddd09ac9fb74b9678915570a5501c3efad Mon Sep 17 00:00:00 2001 From: Jinhao Date: Wed, 7 Nov 2018 06:18:56 +0800 Subject: [PATCH 075/149] add column_resizable and column_movable to listbox --- include/nana/gui/widgets/listbox.hpp | 5 ++ source/gui/widgets/listbox.cpp | 109 +++++++++++++++------------ 2 files changed, 67 insertions(+), 47 deletions(-) diff --git a/include/nana/gui/widgets/listbox.hpp b/include/nana/gui/widgets/listbox.hpp index ae02ef38..ecdb9be8 100644 --- a/include/nana/gui/widgets/listbox.hpp +++ b/include/nana/gui/widgets/listbox.hpp @@ -1461,6 +1461,11 @@ the nana::detail::basic_window member pointer scheme /// Returns the number of columns size_type column_size() const; + void column_resizable(bool resizable); + bool column_resizable() const; + void column_movable(bool); + bool column_movable() const; + /// Returns a rectangle in where the content is drawn. rectangle content_area() const; diff --git a/source/gui/widgets/listbox.cpp b/source/gui/widgets/listbox.cpp index 0c46cd52..deb6d881 100644 --- a/source/gui/widgets/listbox.cpp +++ b/source/gui/widgets/listbox.cpp @@ -119,6 +119,14 @@ namespace nana class es_header { public: + struct attributes + { + bool movable{true}; + bool resizable{true}; + bool sortable{true}; + bool visible{true}; + }; + struct column : public column_interface { @@ -286,28 +294,14 @@ namespace nana return head_str; } - bool visible() const noexcept + const attributes& attrib() const noexcept { - return visible_; + return attrib_; } - bool visible(bool v) noexcept + attributes& attrib() noexcept { - if (visible_ == v) - return false; - - visible_ = v; - return true; - } - - bool sortable() const noexcept - { - return sortable_; - } - - void sortable(bool enable) noexcept - { - sortable_ = enable; + return attrib_; } size_type create(essence* ess, native_string_type&& text, unsigned pixels) @@ -584,8 +578,7 @@ namespace nana } } private: - bool visible_{true}; - bool sortable_{true}; + attributes attrib_; unsigned margin_{ 5 }; container cont_; }; @@ -2316,7 +2309,7 @@ namespace nana { /// we are inside auto const origin = content_view->origin(); - if (header.visible() && (pos.y < static_cast(header_visible_px()) + area.y)) + if (header.attrib().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); @@ -2433,15 +2426,15 @@ namespace nana unsigned header_visible_px() const { - if (!header.visible()) - return 0; + if(header.attrib().visible) + return scheme_ptr->header_padding_top + scheme_ptr->header_padding_bottom + static_cast(header_font_px()); - return scheme_ptr->header_padding_top + scheme_ptr->header_padding_bottom + static_cast(header_font_px()); + return 0; } bool rect_header(nana::rectangle& r) const { - if(header.visible()) + if(header.attrib().visible) { r = this->content_area(); @@ -3234,13 +3227,16 @@ namespace nana } // Detects a header spliter, return true if x is in the splitter area after that header item (column) - bool detect_splitter(const nana::rectangle& r, int x) noexcept + bool detect_splitter(int x) noexcept { + nana::rectangle r; + if (!essence_->rect_header(r)) + return false; + if(essence_->ptr_state == item_state::highlighted) { x -= r.x - essence_->content_view->origin().x + static_cast(essence_->header.margin()); - for(auto & col : essence_->header.cont()) // in current order { if(col.visible_state) @@ -4108,7 +4104,7 @@ namespace nana if (essence_->rect_lister(r)) drawer_lister_->draw(r); - if (essence_->header.visible() && essence_->rect_header(r)) + if (essence_->header.attrib().visible && essence_->rect_header(r)) drawer_header_->draw(graph, r); essence_->draw_peripheral(); @@ -4129,7 +4125,7 @@ namespace nana if(essence_->ptr_state == item_state::pressed) { - if(essence_->pointer_where.first == parts::header) + if((essence_->pointer_where.first == parts::header) && essence_->header.attrib().movable) { // moving a pressed header : grab it essence_->ptr_state = item_state::grabbed; @@ -4152,21 +4148,14 @@ namespace nana need_refresh = true; } - bool set_splitter = false; - if(essence_->pointer_where.first == parts::header) + //Detects column splitter + if(essence_->header.attrib().resizable && + (essence_->pointer_where.first == parts::header) && + drawer_header_->detect_splitter(arg.pos.x)) { - nana::rectangle r; - if(essence_->rect_header(r)) - { - if(drawer_header_->detect_splitter(r, arg.pos.x)) - { - set_splitter = true; - essence_->lister.wd_ptr()->cursor(cursor::size_we); - } - } + essence_->lister.wd_ptr()->cursor(cursor::size_we); } - - if((!set_splitter) && (essence_->ptr_state != item_state::grabbed)) + else if(essence_->ptr_state != item_state::grabbed) { if((drawer_header_->splitter() != npos) || (essence_->lister.wd_ptr()->cursor() == cursor::size_we)) { @@ -4402,7 +4391,7 @@ namespace nana bool need_refresh = false; //Don't sort the column when the mouse is due to released for stopping resizing column. - if ((drawer_header_->splitter() == npos) && essence_->header.sortable() && essence_->pointer_where.first == parts::header && prev_state == item_state::pressed) + if ((drawer_header_->splitter() == npos) && essence_->header.attrib().sortable && essence_->pointer_where.first == parts::header && prev_state == item_state::pressed) { //Try to sort the column if(essence_->pointer_where.second < essence_->header.cont().size()) @@ -5681,6 +5670,31 @@ namespace nana return _m_ess().header.cont().size(); } + + void listbox::column_resizable(bool resizable) + { + internal_scope_guard lock; + _m_ess().header.attrib().resizable = resizable; + } + + bool listbox::column_resizable() const + { + internal_scope_guard lock; + return _m_ess().header.attrib().resizable; + } + + void listbox::column_movable(bool movable) + { + internal_scope_guard lock; + _m_ess().header.attrib().movable = movable; + } + + bool listbox::column_movable() const + { + internal_scope_guard lock; + return _m_ess().header.attrib().movable; + } + //Contributed by leobackes(pr#97) listbox::size_type listbox::column_from_pos ( const point& pos ) const { @@ -5855,13 +5869,13 @@ namespace nana bool listbox::sortable() const { internal_scope_guard lock; - return _m_ess().header.sortable(); + return _m_ess().header.attrib().sortable; } void listbox::sortable(bool enable) { internal_scope_guard lock; - _m_ess().header.sortable(enable); + _m_ess().header.attrib().sortable = enable; } void listbox::set_sort_compare(size_type col, std::function strick_ordering) @@ -5905,13 +5919,14 @@ namespace nana void listbox::show_header(bool sh) { internal_scope_guard lock; - _m_ess().header.visible(sh); + _m_ess().header.attrib().visible = sh; _m_ess().update(); } bool listbox::visible_header() const { - return _m_ess().header.visible(); + internal_scope_guard lock; + return _m_ess().header.attrib().visible; } void listbox::move_select(bool upwards) /// Date: Wed, 7 Nov 2018 16:03:50 +0100 Subject: [PATCH 076/149] emit tree node selected only after mouse_down and checked this avoid inconsistent drawing or too much redrawing --- source/gui/widgets/treebox.cpp | 36 +++++++++++++++------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/source/gui/widgets/treebox.cpp b/source/gui/widgets/treebox.cpp index 3cbf61b7..de38a7b9 100644 --- a/source/gui/widgets/treebox.cpp +++ b/source/gui/widgets/treebox.cpp @@ -1863,7 +1863,7 @@ namespace nana } else if (node_state.selected != node_state.pressed_node) { - impl_->set_selected(node_state.pressed_node); + //impl_->set_selected(node_state.pressed_node); // todo: emit selected after checked } else return; @@ -1886,27 +1886,23 @@ namespace nana if(!nl.node()) return; - if (pressed_node == nl.node()) - { - if ((impl_->node_state.selected != nl.node()) && nl.item_body()) - { - impl_->set_selected(nl.node()); - if (impl_->make_adjust(impl_->node_state.selected, 1)) - impl_->adjust.scroll_timestamp = 1; - } - else if (nl.what() == component::crook) - { - checkstate cs = checkstate::unchecked; - if (checkstate::unchecked == nl.node()->value.second.checked) - cs = checkstate::checked; + if (pressed_node != nl.node()) + return; //Do not refresh - check(nl.node(), cs); - } - else - return; //Do not refresh + if (nl.what() == component::crook) + { + checkstate cs = checkstate::unchecked; + if (checkstate::unchecked == nl.node()->value.second.checked) + cs = checkstate::checked; + + check(nl.node(), cs); + } + if ((impl_->node_state.selected != nl.node()) && (nl.item_body() || nl.what() == component::crook)) + { + impl_->set_selected(nl.node()); + if (impl_->make_adjust(impl_->node_state.selected, 1)) + impl_->adjust.scroll_timestamp = 1; } - else - return; //Don't refresh impl_->draw(true); API::dev::lazy_refresh(); From e4a829d6f847ac4790b54aecfd9e28d694c1659d Mon Sep 17 00:00:00 2001 From: qPCR4vir Date: Wed, 7 Nov 2018 16:09:49 +0100 Subject: [PATCH 077/149] FIX crash while deleting a selected tree node --- include/nana/gui/widgets/detail/tree_cont.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/nana/gui/widgets/detail/tree_cont.hpp b/include/nana/gui/widgets/detail/tree_cont.hpp index 2490ab95..d3993411 100644 --- a/include/nana/gui/widgets/detail/tree_cont.hpp +++ b/include/nana/gui/widgets/detail/tree_cont.hpp @@ -63,7 +63,7 @@ namespace detail { while (child) { - if (child->owner == this) + if (child == this) return true; child = child->owner; From ab789837a008d9890bc479b1e1dc5e2822d386fe Mon Sep 17 00:00:00 2001 From: qPCR4vir Date: Wed, 7 Nov 2018 19:37:05 +0100 Subject: [PATCH 078/149] cmake-3.12 in travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 42f63050..9d7e849e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -44,7 +44,7 @@ before_install: - 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 + - wget --no-check-certificate --no-clobber -O /tmp/tools/cmake https://cmake.org/files/v3.12/cmake-3.12.0-rc3-Linux-x86_64.sh || true - chmod -R +x /tmp/tools install: From e4b60148e76cc91cbf1c0b66bd715e9329f79490 Mon Sep 17 00:00:00 2001 From: qPCR4vir Date: Tue, 13 Nov 2018 19:01:20 +0100 Subject: [PATCH 079/149] listbox::move_column implemented by new es_header::move_to_view_pos () --- include/nana/gui/widgets/listbox.hpp | 6 ++- source/gui/widgets/listbox.cpp | 58 ++++++++++++++++++++-------- 2 files changed, 47 insertions(+), 17 deletions(-) diff --git a/include/nana/gui/widgets/listbox.hpp b/include/nana/gui/widgets/listbox.hpp index ecdb9be8..7622f331 100644 --- a/include/nana/gui/widgets/listbox.hpp +++ b/include/nana/gui/widgets/listbox.hpp @@ -1461,6 +1461,9 @@ the nana::detail::basic_window member pointer scheme /// Returns the number of columns size_type column_size() const; + /// Move column to view_position + void move_column(size_type abs_pos, size_type view_pos); + void column_resizable(bool resizable); bool column_resizable() const; void column_movable(bool); @@ -1517,7 +1520,8 @@ the nana::detail::basic_window member pointer scheme ///Sets a strict weak ordering comparer for a column void set_sort_compare( size_type col, - std::function strick_ordering); + std::function strick_ordering); /// sort() and ivalidate any existing reference from display position to absolute item, that is: after sort() display offset point to different items void sort_col(size_type col, bool reverse = false); diff --git a/source/gui/widgets/listbox.cpp b/source/gui/widgets/listbox.cpp index deb6d881..fe0f96d0 100644 --- a/source/gui/widgets/listbox.cpp +++ b/source/gui/widgets/listbox.cpp @@ -17,6 +17,12 @@ * dankan1890(pr#158) * */ +#include +#include +#include +#include +#include +#include #include #include //for inline widget @@ -28,12 +34,6 @@ #include #include "skeletons/content_view.hpp" -#include -#include -#include -#include -#include - namespace nana { static void check_range(std::size_t pos, std::size_t size) @@ -130,17 +130,18 @@ namespace nana struct column : public column_interface { - native_string_type caption; - unsigned width_px; - std::pair range_width_px; + native_string_type caption; //< header title + unsigned width_px; //< column width in pixels + std::pair range_width_px; //< allowed witdh bool visible_state{ true }; - /// Absolute position of column when it was creating - size_type index; + + size_type index; //< Absolute position of column when it was created nana::align alignment{ nana::align::left }; - std::function weak_ordering; + std::function weak_ordering; std::shared_ptr font; ///< The exclusive column font @@ -186,18 +187,18 @@ namespace nana { } private: - //The definition is provided after essence + /// The definition is provided after essence void _m_refresh() noexcept; private: essence* const ess_; public: - //Implementation of column_interface + /// Implementation of column_interface unsigned width() const noexcept override { return width_px; } - // Sets the width and overrides the ranged width + /// Sets the width and overrides the ranged width void width(unsigned pixels) noexcept override { width_px = pixels; @@ -223,7 +224,7 @@ namespace nana } } - size_type position(bool disp_order) const noexcept override; //The definition is provided after essence + size_type position(bool disp_order) const noexcept override; //< The definition is provided after essence std::string text() const noexcept override { @@ -542,6 +543,24 @@ namespace nana return pos; } + + /// move col to view pos + void move_to_view_pos (size_type col, size_type view, bool front) noexcept + { + if (!front) view++; + if (view >= cont_.size() ) return; + + auto i = std::find_if( cont_.begin(), + cont_.end(), + [&](const column& c){return col==c.index;}); + + if (i==cont_.end()) return; + + auto col_from = *i; + cont_.erase(i); + cont_.insert(cont_.begin()+ view, col_from); + + } /// move the col originaly at "from" to the position currently in front (or after) the col originaly at index "to" invalidating some current index void move(size_type from, size_type to, bool front) noexcept { @@ -6049,5 +6068,12 @@ namespace nana internal_scope_guard lock; return _m_ess().content_view->scroll_operation(); } + + /// Move column to view_position + void listbox::move_column(size_type abs_pos, size_type view_pos) + { + internal_scope_guard lock; + return _m_ess().header.move_to_view_pos(abs_pos, view_pos, true); + } //end class listbox }//end namespace nana From c0de9ee4602e86757985b19375cc301de9fff01d Mon Sep 17 00:00:00 2001 From: qPCR4vir Date: Tue, 13 Nov 2018 19:05:19 +0100 Subject: [PATCH 080/149] listbox::reorder_columns implemented by move_column --- include/nana/gui/widgets/listbox.hpp | 11 ++++++- source/gui/widgets/listbox.cpp | 44 ++++++++++++++++++++++++---- 2 files changed, 48 insertions(+), 7 deletions(-) diff --git a/include/nana/gui/widgets/listbox.hpp b/include/nana/gui/widgets/listbox.hpp index 7622f331..cae6ec92 100644 --- a/include/nana/gui/widgets/listbox.hpp +++ b/include/nana/gui/widgets/listbox.hpp @@ -1464,7 +1464,16 @@ the nana::detail::basic_window member pointer scheme /// Move column to view_position void move_column(size_type abs_pos, size_type view_pos); - void column_resizable(bool resizable); + /// Sort columns in range first_col to last_col inclusive using the values from a row + void reorder_columns(size_type first_col, + size_type last_col, + index_pair row, bool reverse, + std::function comp); + + void column_resizable(bool resizable); bool column_resizable() const; void column_movable(bool); bool column_movable() const; diff --git a/source/gui/widgets/listbox.cpp b/source/gui/widgets/listbox.cpp index fe0f96d0..e6c7e97a 100644 --- a/source/gui/widgets/listbox.cpp +++ b/source/gui/widgets/listbox.cpp @@ -6069,11 +6069,43 @@ namespace nana return _m_ess().content_view->scroll_operation(); } - /// Move column to view_position - void listbox::move_column(size_type abs_pos, size_type view_pos) - { - internal_scope_guard lock; - return _m_ess().header.move_to_view_pos(abs_pos, view_pos, true); - } + /// Move column to view_position + void listbox::move_column(size_type abs_pos, size_type view_pos) + { + internal_scope_guard lock; + return _m_ess().header.move_to_view_pos(abs_pos, view_pos, true); + } + + /// Sort columns in range first_col to last_col inclusive using a row + void listbox::reorder_columns(size_type first_col, + size_type last_col, + index_pair row, bool reverse, + std::function comp) + { + if (first_col<0 || last_col<=first_col) + return; + if (last_col >= column_size()) + return; + std::vector new_idx; + for(size_type i=first_col; i<=last_col; ++i) new_idx.push_back(i); + const item_proxy & ip_row=this->at(row); + internal_scope_guard lock; + const nana::any *pnany=_m_ess().lister.anyobj(row,false); + std::sort(new_idx.begin(), new_idx.end(), [&](size_type col1, + size_type col2) + { + return comp(ip_row.text(col1), col1, + ip_row.text(col2), col2, + pnany, reverse); + }); + for(size_t i=0; i Date: Tue, 13 Nov 2018 19:42:07 +0100 Subject: [PATCH 081/149] update --- source/gui/widgets/listbox.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/gui/widgets/listbox.cpp b/source/gui/widgets/listbox.cpp index e6c7e97a..dd970a65 100644 --- a/source/gui/widgets/listbox.cpp +++ b/source/gui/widgets/listbox.cpp @@ -6074,6 +6074,7 @@ namespace nana { internal_scope_guard lock; return _m_ess().header.move_to_view_pos(abs_pos, view_pos, true); + _m_ess().update(); } /// Sort columns in range first_col to last_col inclusive using a row @@ -6105,6 +6106,7 @@ namespace nana { move_column(new_idx[i],i+first_col); } + _m_ess().update(); } //end class listbox From 1c5f70124cc996b9abb538f8ab7f343f6b693513 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Fri, 16 Nov 2018 05:27:53 +0800 Subject: [PATCH 082/149] fix issue that blur at wrong x-axis --- include/nana/paint/detail/image_processor.hpp | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/include/nana/paint/detail/image_processor.hpp b/include/nana/paint/detail/image_processor.hpp index 3832456f..1a412a2e 100644 --- a/include/nana/paint/detail/image_processor.hpp +++ b/include/nana/paint/detail/image_processor.hpp @@ -562,18 +562,18 @@ namespace detail int large_edge = (w > h ? w : h); const int div_256 = div * 256; - std::unique_ptr all_table(new int[(wh << 1) + wh + (large_edge << 1) + div_256]); + std::unique_ptr table_rgb(new int[(wh << 1) + wh + (large_edge << 1) + div_256]); - int * r = all_table.get(); - int * g = r + wh; - int * b = g + wh; + int * tbl_r = table_rgb.get(); + int * tbl_g = tbl_r + wh; + int * tbl_b = tbl_g + wh; - int * vmin = b + wh; + int * vmin = tbl_b + wh; int * vmax = vmin + large_edge; int * dv = vmax + large_edge; - int end_div = div - 1; + const int end_div = div - 1; for(int i = 0, *dv_block = dv; i < 256; ++i) { for(int u = 0; u < end_div; u += 2) @@ -614,9 +614,9 @@ namespace detail for(int x = 0; x < w; ++x) { - r[yi] = dv[sum_r]; - g[yi] = dv[sum_g]; - b[yi] = dv[sum_b]; + tbl_r[yi] = dv[sum_r]; + tbl_g[yi] = dv[sum_g]; + tbl_b[yi] = dv[sum_b]; if(0 == y) { @@ -647,21 +647,21 @@ namespace detail { if(yp < 1) { - sum_r += r[x]; - sum_g += g[x]; - sum_b += b[x]; + sum_r += tbl_r[x]; + sum_g += tbl_g[x]; + sum_b += tbl_b[x]; } else { int yi = yp + x; - sum_r += r[yi]; - sum_g += g[yi]; - sum_b += b[yi]; + sum_r += tbl_r[yi]; + sum_g += tbl_g[yi]; + sum_b += tbl_b[yi]; } yp += w; } - linepix = pixbuf.raw_ptr(area.y) + x; + linepix = pixbuf.raw_ptr(area.y) + x + area.x; for(int y = 0; y < h; ++y) { @@ -675,9 +675,9 @@ namespace detail int pt1 = x + vmin[y]; int pt2 = x + vmax[y]; - sum_r += r[pt1] - r[pt2]; - sum_g += g[pt1] - g[pt2]; - sum_b += b[pt1] - b[pt2]; + sum_r += tbl_r[pt1] - tbl_r[pt2]; + sum_g += tbl_g[pt1] - tbl_g[pt2]; + sum_b += tbl_b[pt1] - tbl_b[pt2]; linepix = pixel_at(linepix, bytes_pl); } From fe01aa0fd98e86755328a12ec865cbeea111934d Mon Sep 17 00:00:00 2001 From: Jinhao Date: Fri, 16 Nov 2018 07:41:29 +0800 Subject: [PATCH 083/149] add class dragdrop --- include/nana/gui/dragdrop.hpp | 22 ++++- source/gui/dragdrop.cpp | 153 +++++++++++++++++++++++++++++++++- 2 files changed, 173 insertions(+), 2 deletions(-) diff --git a/include/nana/gui/dragdrop.hpp b/include/nana/gui/dragdrop.hpp index e68279b8..96ed24ab 100644 --- a/include/nana/gui/dragdrop.hpp +++ b/include/nana/gui/dragdrop.hpp @@ -31,7 +31,7 @@ namespace nana simple_dragdrop(simple_dragdrop&&) = delete; simple_dragdrop& operator=(simple_dragdrop&&) = delete; public: - simple_dragdrop(window drag_wd); + simple_dragdrop(window source); ~simple_dragdrop(); /// Sets a condition that determines whether the drag&drop can start @@ -40,6 +40,26 @@ namespace nana private: implementation* const impl_; }; + + class dragdrop + { + struct implementation; + + dragdrop(const dragdrop&) = delete; + dragdrop& operator=(const dragdrop&) = delete; + + dragdrop(dragdrop&&) = delete; + dragdrop& operator=(dragdrop&&) = delete; + public: + dragdrop(window source); + ~dragdrop(); + + void condition(std::function predicate_fn); + + void make_data(std::function generator); + private: + implementation* const impl_; + }; } #endif \ No newline at end of file diff --git a/source/gui/dragdrop.cpp b/source/gui/dragdrop.cpp index 4751a62a..e6d653a3 100644 --- a/source/gui/dragdrop.cpp +++ b/source/gui/dragdrop.cpp @@ -225,6 +225,36 @@ namespace nana FORMATETC * format; }; public: + ~drop_data() + { + if(hglobal_) + ::GlobalFree(hglobal_); + } + + void assign(const std::vector& files) + { + std::size_t bytes = 0; + for (auto & f : files) + bytes += (f.size() + 1) * sizeof(f.front()); + + hglobal_ = ::GlobalAlloc(GHND | GMEM_SHARE, sizeof(DROPFILES) + bytes); + + auto dropfiles = reinterpret_cast(::GlobalLock(hglobal_)); + dropfiles->pFiles = sizeof(DROPFILES); + dropfiles->fWide = true; + + auto file_buf = reinterpret_cast(dropfiles) + sizeof(DROPFILES); + + for (auto & f : files) + { + std::memcpy(file_buf, f.data(), (f.size() + 1) * sizeof(f.front())); + file_buf += f.size() + 1; + } + + ::GlobalUnlock(hglobal_); + } + public: + // Implement IDataObject STDMETHODIMP GetData(FORMATETC *request_format, STGMEDIUM *pmedium) override { if (!(request_format && pmedium)) @@ -385,8 +415,11 @@ namespace nana return S_OK; } private: + HGLOBAL hglobal_{nullptr}; std::vector mediums_; }; + + #elif defined(NANA_X11) class x11_dragdrop: public detail::x11_dragdrop_interface, public dragdrop_session { @@ -897,4 +930,122 @@ namespace nana impl_->ddrop->insert(impl_->window_handle, target); impl_->targets[target].swap(drop_fn); } -} \ No newline at end of file + + + + + + + + //This is class dragdrop + struct dragdrop::implementation + { + window source_handle; + bool dragging{ false }; + std::function predicate; + + std::function generator; + + + struct event_handlers + { + nana::event_handle destroy; + nana::event_handle mouse_move; + nana::event_handle mouse_down; + }events; + + void make_data() + { + //https://www.codeproject.com/Articles/840/How-to-Implement-Drag-and-Drop-Between-Your-Progra + + std::vector files; + files.push_back(L"D:\\universal_access"); + files.push_back(L"D:\\н¨Îı¾Îĵµ.cpp"); + + std::size_t bytes = 0; + for (auto & f : files) + bytes += (f.size() + 1) * sizeof(f.front()); + + auto data_handle = ::GlobalAlloc(GHND | GMEM_SHARE, sizeof(DROPFILES) + bytes); + + auto dropfiles = reinterpret_cast(::GlobalLock(data_handle)); + dropfiles->pFiles = sizeof(DROPFILES); + dropfiles->fWide = true; + + auto file_buf = reinterpret_cast(dropfiles) + sizeof(DROPFILES); + + for (auto & f : files) + { + std::memcpy(file_buf, f.data(), (f.size() + 1) * sizeof(f.front())); + file_buf += f.size() + 1; + } + + ::GlobalUnlock(data_handle); + + + generator(); + } + }; + + dragdrop::dragdrop(window source) : + impl_(new implementation) + { + impl_->source_handle = source; + + auto & events = API::events(source); + impl_->events.destroy = events.destroy.connect_unignorable([this](const arg_destroy&) { + dragdrop_service::instance().remove(impl_->source_handle); + API::dev::window_draggable(impl_->source_handle, false); + }); + + impl_->events.mouse_down = events.mouse_down.connect_unignorable([this](const arg_mouse& arg) { + if (arg.is_left_button() && API::is_window(impl_->source_handle)) + { + impl_->dragging = ((!impl_->predicate) || impl_->predicate()); + + using basic_window = ::nana::detail::basic_window; + auto real_wd = reinterpret_cast<::nana::detail::basic_window*>(impl_->source_handle); + real_wd->other.dnd_state = dragdrop_status::ready; + } + }); + + impl_->events.mouse_move = events.mouse_move.connect_unignorable([this](const arg_mouse& arg) { + if (!(arg.is_left_button() && impl_->dragging && API::is_window(arg.window_handle))) + return; + + using basic_window = ::nana::detail::basic_window; + auto real_wd = reinterpret_cast<::nana::detail::basic_window*>(arg.window_handle); + real_wd->other.dnd_state = dragdrop_status::in_progress; + + impl_->make_data(); + + auto has_dropped = dragdrop_service::instance().dragdrop(arg.window_handle); + + real_wd->other.dnd_state = dragdrop_status::not_ready; + impl_->dragging = false; + + if (has_dropped) + { + auto drop_wd = API::find_window(API::cursor_position()); + //auto i = impl_->targets.find(drop_wd); + //if ((impl_->targets.end() != i) && i->second) + // i->second(); + } + }); + } + + dragdrop::~dragdrop() + { + delete impl_; + } + + void dragdrop::condition(std::function predicate_fn) + { + + } + + void dragdrop::make_data(std::function generator) + { + impl_->generator = generator; + } +}//end namespace nana \ No newline at end of file From ca9e597e376f1546cfbf1347979542c76e3e74ae Mon Sep 17 00:00:00 2001 From: Jinhao Date: Sat, 17 Nov 2018 06:06:01 +0800 Subject: [PATCH 084/149] improve performance of blur algorithm --- include/nana/paint/detail/image_processor.hpp | 155 +++++++++--------- 1 file changed, 76 insertions(+), 79 deletions(-) diff --git a/include/nana/paint/detail/image_processor.hpp b/include/nana/paint/detail/image_processor.hpp index 1a412a2e..1638c25d 100644 --- a/include/nana/paint/detail/image_processor.hpp +++ b/include/nana/paint/detail/image_processor.hpp @@ -1,7 +1,7 @@ /* * Image Processor Algorithm 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 @@ -551,55 +551,65 @@ namespace detail { void process(pixel_buffer& pixbuf, const nana::rectangle& area, std::size_t u_radius) const { - int radius = static_cast(u_radius); - int w = area.width; - int h = area.height; - int wm = w - 1; - int hm = h - 1; - int wh = w * h; - int div = (radius << 1) + 1; + const int radius = static_cast(u_radius); + const int safe_radius = std::min(radius, static_cast(area.height) - 2); + const int radius_plus_one = radius + 1; - int large_edge = (w > h ? w : h); - const int div_256 = div * 256; + const int width_3times = static_cast(area.width * 3); + const int wm = area.width - 1; + const int hm = area.height - 1; + const int wh = area.width * area.height; + const int div = (radius << 1) + 1; - std::unique_ptr table_rgb(new int[(wh << 1) + wh + (large_edge << 1) + div_256]); + const int large_edge = std::max(area.width, area.height); + std::unique_ptr table_rgb(new int[wh * 3 + (large_edge << 1) + div * 256]); - int * tbl_r = table_rgb.get(); - int * tbl_g = tbl_r + wh; - int * tbl_b = tbl_g + wh; - - int * vmin = tbl_b + wh; - int * vmax = vmin + large_edge; + int * tbl_rgb = table_rgb.get(); + int * const vmin = tbl_rgb + 3 * wh; + int * const vmax = vmin + large_edge; int * dv = vmax + large_edge; - const int end_div = div - 1; - for(int i = 0, *dv_block = dv; i < 256; ++i) + + for (int i = 0; i < 256; ++i) { - for(int u = 0; u < end_div; u += 2) + dv[0] = i; + for (int u = 1; u < div; u += 2) { - dv_block[u] = i; - dv_block[u + 1] = i; + dv[u] = i; + dv[u + 1] = i; } - dv_block[div - 1] = i; - dv_block += div; + dv += div; } + dv = vmax + large_edge; + auto linepix = pixbuf.raw_ptr(area.y) + area.x; - int yi = 0; - for(int y = 0; y < h; ++y) + for(int x = 0; x < static_cast(area.width); ++x) + { + vmin[x] = std::min(x + radius_plus_one, wm); + vmax[x] = std::max(x - radius, 0); + } + + for(int y = 0; y < static_cast(area.height); ++y) { int sum_r = 0, sum_g = 0, sum_b = 0; if(radius <= wm) { - for(int i = - radius; i <= radius; ++i) + auto px = linepix; + + sum_r = int(px->element.red) * radius_plus_one; + sum_g = int(px->element.blue) * radius_plus_one; + sum_b = int(px->element.blue) * radius_plus_one; + + auto radius_px_end = px + radius_plus_one; + for (++px; px < radius_px_end; ++px) { - auto px = linepix[(i > 0 ? i : 0)]; - sum_r += px.element.red; - sum_g += px.element.green; - sum_b += px.element.blue; - } + sum_r += px->element.red; + sum_g += px->element.green; + sum_b += px->element.blue; + } } else { @@ -612,75 +622,62 @@ namespace detail } } - for(int x = 0; x < w; ++x) + for(int x = 0; x < static_cast(area.width); ++x) { - tbl_r[yi] = dv[sum_r]; - tbl_g[yi] = dv[sum_g]; - tbl_b[yi] = dv[sum_b]; + tbl_rgb[0] = dv[sum_r]; + tbl_rgb[1] = dv[sum_g]; + tbl_rgb[2] = dv[sum_b]; + tbl_rgb += 3; - if(0 == y) - { - vmin[x] = std::min(x + radius + 1, wm); - vmax[x] = std::max(x - radius, 0); - } - - auto p1 = linepix[vmin[x]]; - auto p2 = linepix[vmax[x]]; + auto& p1 = linepix[vmin[x]]; + auto& p2 = linepix[vmax[x]]; sum_r += p1.element.red - p2.element.red; sum_g += p1.element.green - p2.element.green; sum_b += p1.element.blue - p2.element.blue; - ++yi; } linepix = pixbuf.raw_ptr(area.y + y) + area.x; } - const int yp_init = -radius * w; - const std::size_t bytes_pl = pixbuf.bytes_per_line(); - for(int x = 0; x < w; ++x) - { - int sum_r = 0, sum_g = 0, sum_b = 0; - int yp = yp_init; - for(int i = -radius; i <= radius; ++i) + tbl_rgb = table_rgb.get(); + + for (int y = 0; y < static_cast(area.height); ++y) + { + vmin[y] = std::min(y + radius_plus_one, hm) * width_3times; + vmax[y] = std::max(y - radius, 0) * width_3times; + } + + for(int x = 0; x < static_cast(area.width); ++x) + { + int sum_r = int(tbl_rgb[0]) * radius_plus_one; + int sum_g = int(tbl_rgb[1]) * radius_plus_one; + int sum_b = int(tbl_rgb[2]) * radius_plus_one; + + int nextln = width_3times; + for (int i = 0; i < safe_radius; ++i) { - if(yp < 1) - { - sum_r += tbl_r[x]; - sum_g += tbl_g[x]; - sum_b += tbl_b[x]; - } - else - { - int yi = yp + x; - sum_r += tbl_r[yi]; - sum_g += tbl_g[yi]; - sum_b += tbl_b[yi]; - } - yp += w; + sum_r += tbl_rgb[nextln]; + sum_g += tbl_rgb[nextln + 1]; + sum_b += tbl_rgb[nextln + 2]; + nextln += width_3times; } linepix = pixbuf.raw_ptr(area.y) + x + area.x; - - for(int y = 0; y < h; ++y) + for(int y = 0; y < static_cast(area.height); ++y) { linepix->value = 0xFF000000 | (dv[sum_r] << 16) | (dv[sum_g] << 8) | dv[sum_b]; - if(x == 0) - { - vmin[y] = std::min(y + radius + 1, hm) * w; - vmax[y] = std::max(y - radius, 0) * w; - } - int pt1 = x + vmin[y]; - int pt2 = x + vmax[y]; - - sum_r += tbl_r[pt1] - tbl_r[pt2]; - sum_g += tbl_g[pt1] - tbl_g[pt2]; - sum_b += tbl_b[pt1] - tbl_b[pt2]; + int pt1 = vmin[y]; + int pt2 = vmax[y]; + sum_r += tbl_rgb[pt1] - tbl_rgb[pt2]; + sum_g += tbl_rgb[pt1 + 1] - tbl_rgb[pt2 + 1]; + sum_b += tbl_rgb[pt1 + 2] - tbl_rgb[pt2 + 2]; linepix = pixel_at(linepix, bytes_pl); } + tbl_rgb += 3; } } };//end class superfast_blur From dec3bdc3500b5a94956b6850be5766f4511e5ad7 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Sat, 17 Nov 2018 06:07:27 +0800 Subject: [PATCH 085/149] remove compiler warnings --- include/nana/gui/widgets/detail/tree_cont.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/nana/gui/widgets/detail/tree_cont.hpp b/include/nana/gui/widgets/detail/tree_cont.hpp index 2490ab95..7dab9d4d 100644 --- a/include/nana/gui/widgets/detail/tree_cont.hpp +++ b/include/nana/gui/widgets/detail/tree_cont.hpp @@ -357,12 +357,12 @@ namespace detail } template - unsigned distance_if(const node_type * node, PredAllowChild pac) const + std::size_t distance_if(const node_type * node, PredAllowChild pac) const { if(nullptr == node) return 0; const node_type * iterator = root_.child; - unsigned off = 0; + std::size_t off = 0; std::stack stack; while(iterator && iterator != node) From e72bec2c8f60300570ef3082c0e8881951219669 Mon Sep 17 00:00:00 2001 From: James Bremner Date: Sun, 25 Nov 2018 13:52:00 -0500 Subject: [PATCH 086/149] Add method to check option in group --- include/nana/gui/widgets/group.hpp | 15 +++++++++++---- source/gui/widgets/group.cpp | 12 +++++++++--- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/include/nana/gui/widgets/group.hpp b/include/nana/gui/widgets/group.hpp index 2e264289..6d20fb83 100644 --- a/include/nana/gui/widgets/group.hpp +++ b/include/nana/gui/widgets/group.hpp @@ -3,8 +3,8 @@ * Nana C++ Library(http://www.nanaro.org) * Copyright(C) 2015 Jinhao(cnjinhao@hotmail.com) * - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) * * @file: nana/gui/widgets/group.hpp @@ -55,9 +55,16 @@ namespace nana{ /// Enables/disables the radio mode which is single selection group& radio_mode(bool); - /// Returns the index of option in radio_mode, it throws a logic_error if radio_mode is false. + /// Returns the index of selected option in radio_mode, it throws a logic_error if radio_mode is false. std::size_t option() const; + /** Set check/unchecked status of specified option + @param[in] pos zero-based index of option to set + @param[in] check status required, defaults to checked + throws an out_of_range if !(pos < number of options) + */ + void option_check( std::size_t pos, bool check = true ); + /// Determines whether a specified option is checked, it throws an out_of_range if !(pos < number of options) bool option_checked(std::size_t pos) const; @@ -66,7 +73,7 @@ namespace nana{ group& collocate() throw(); group& div(const char* div_str) throw(); field_reference operator[](const char* field); - + template Widget* create_child(const char* field, Args && ... args) { diff --git a/source/gui/widgets/group.cpp b/source/gui/widgets/group.cpp index ab8e9e73..d2666383 100644 --- a/source/gui/widgets/group.cpp +++ b/source/gui/widgets/group.cpp @@ -3,8 +3,8 @@ * Nana C++ Library(http://www.nanaro.org) * Copyright(C) 2015-2017 Jinhao(cnjinhao@hotmail.com) * - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) * * @file: nana/gui/widgets/group.cpp @@ -14,7 +14,7 @@ * @brief group is a widget used to visually group and layout other widgets. * * @contributor: - * dankan1890(https://github.com/dankan1890) + * dankan1890(https://github.com/dankan1890) */ @@ -160,6 +160,12 @@ namespace nana{ throw std::logic_error("the radio_mode of the group is disabled"); } + void group::option_check( std::size_t pos, bool check ) + { + _THROW_IF_EMPTY(); + return impl_->options.at(pos)->check( check ); + } + bool group::option_checked(std::size_t pos) const { _THROW_IF_EMPTY(); From a4c9c0ac8ee56d073e03094affeae35b39d7b3c2 Mon Sep 17 00:00:00 2001 From: James Bremner Date: Sun, 25 Nov 2018 15:04:01 -0500 Subject: [PATCH 087/149] Restore some lost lines --- include/nana/gui/widgets/group.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/nana/gui/widgets/group.hpp b/include/nana/gui/widgets/group.hpp index 6d20fb83..97f3caf9 100644 --- a/include/nana/gui/widgets/group.hpp +++ b/include/nana/gui/widgets/group.hpp @@ -74,6 +74,10 @@ namespace nana{ group& div(const char* div_str) throw(); field_reference operator[](const char* field); + void field_display(const char* field_name, bool display); /// Widget* create_child(const char* field, Args && ... args) { From f20ef2dfb60be9d5a433b759b85f0dbbf2b36e38 Mon Sep 17 00:00:00 2001 From: besh81 Date: Wed, 28 Nov 2018 16:57:56 +0100 Subject: [PATCH 088/149] Added tab_click event to tabbar control tab_click event is processed when a tab is clicked --- include/nana/gui/widgets/tabbar.hpp | 61 +++++++++++++++++++++++------ source/gui/widgets/tabbar.cpp | 14 +++++-- 2 files changed, 59 insertions(+), 16 deletions(-) diff --git a/include/nana/gui/widgets/tabbar.hpp b/include/nana/gui/widgets/tabbar.hpp index 0266ba20..093993f4 100644 --- a/include/nana/gui/widgets/tabbar.hpp +++ b/include/nana/gui/widgets/tabbar.hpp @@ -29,18 +29,44 @@ namespace nana { tabbar & widget; T & value; + std::size_t pos; ///< position of the item - arg_tabbar(tabbar& wdg, T& v) - : widget(wdg), value{ v } + arg_tabbar(tabbar& wdg, T& v, std::size_t p) + : widget(wdg), value{ v }, pos(p) {} }; + template + struct arg_tabbar_click : public arg_tabbar + { + arg_tabbar_click(tabbar& wdg, T& v, std::size_t p) + : arg_tabbar({wdg, v, p}) + {} + + bool left_button = false; ///< true if mouse left button is pressed + bool mid_button = false; ///< true if mouse middle button is pressed + bool right_button = false; ///< true if mouse right button is pressed + }; + + template + struct arg_tabbar_mouse + : public arg_mouse + { + arg_tabbar_mouse(const arg_mouse& arg, tabbar& wdg, T& v, std::size_t p) + : arg_mouse{ arg }, widget(wdg), value{ v }, pos(p) + {} + + tabbar & widget; + T & value; + std::size_t pos; ///< position of the item + }; + template struct arg_tabbar_adding : public event_arg { - arg_tabbar_adding(tabbar& wdg, std::size_t pos) - : widget(wdg), where(pos) + arg_tabbar_adding(tabbar& wdg, std::size_t p) + : widget(wdg), where(p) {} tabbar & widget; @@ -51,8 +77,8 @@ namespace nana template struct arg_tabbar_removed : public arg_tabbar { - arg_tabbar_removed(tabbar& wdg, T& v) - : arg_tabbar({wdg, v}) + arg_tabbar_removed(tabbar& wdg, T& v, std::size_t p) + : arg_tabbar({wdg, v, p}) {} mutable bool remove = true; ///< determines whether to remove the item @@ -71,6 +97,7 @@ namespace nana basic_event> adding; basic_event> added; + basic_event> tab_click; basic_event> activated; basic_event> removed; }; @@ -81,6 +108,7 @@ namespace nana virtual ~event_agent_interface() = default; virtual bool adding(std::size_t) = 0; virtual void added(std::size_t) = 0; + virtual bool click(const arg_mouse&, std::size_t) = 0; virtual void activated(std::size_t) = 0; virtual bool removed(std::size_t, bool & close_attached) = 0; }; @@ -124,9 +152,9 @@ namespace nana bool adding(std::size_t pos) override { - ::nana::arg_tabbar_adding arg(tabbar_, pos); - tabbar_.events().adding.emit(arg, tabbar_); - return arg.add; + ::nana::arg_tabbar_adding arg_ta(tabbar_, pos); + tabbar_.events().adding.emit(arg_ta, tabbar_); + return arg_ta.add; } void added(std::size_t pos) override @@ -134,21 +162,28 @@ namespace nana if(pos != npos) { drawer_trigger_.at_no_bound_check(pos) = T(); - tabbar_.events().added.emit(arg_tabbar({ tabbar_, tabbar_[pos] }), tabbar_); + tabbar_.events().added.emit(arg_tabbar({ tabbar_, tabbar_[pos], pos }), tabbar_); } } + bool click(const arg_mouse& arg, std::size_t pos) override + { + ::nana::arg_tabbar_mouse arg_tm(arg, tabbar_, tabbar_[pos], pos); + tabbar_.events().tab_click.emit(arg_tm, tabbar_); + return arg_tm.propagation_stopped(); + } + void activated(std::size_t pos) override { if(pos != npos) - tabbar_.events().activated.emit(arg_tabbar({ tabbar_, tabbar_[pos]}), tabbar_); + tabbar_.events().activated.emit(arg_tabbar({ tabbar_, tabbar_[pos], pos}), tabbar_); } bool removed(std::size_t pos, bool & close_attach) override { - if (pos != npos) + if(pos != npos) { - ::nana::arg_tabbar_removed arg(tabbar_, tabbar_[pos]); + ::nana::arg_tabbar_removed arg(tabbar_, tabbar_[pos], pos); tabbar_.events().removed.emit(arg, tabbar_); close_attach = arg.close_attach_window; return arg.remove; diff --git a/source/gui/widgets/tabbar.cpp b/source/gui/widgets/tabbar.cpp index d5a05907..cf58e0f6 100644 --- a/source/gui/widgets/tabbar.cpp +++ b/source/gui/widgets/tabbar.cpp @@ -492,9 +492,17 @@ namespace nana return (trace_.what != trace_.null); } - bool active_by_trace() + bool active_by_trace(const arg_mouse& arg) { - return ((trace_.what == trace_.item) && (trace_.item_part != trace_.close)? activate(trace_.u.index) : false); + if((trace_.what == trace_.item) && (trace_.item_part != trace_.close)) + { + if(false == evt_agent_->click(arg, trace_.u.index)) + return activate(trace_.u.index); + + return true; + } + + return false; } bool release() @@ -1285,7 +1293,7 @@ namespace nana { if(layouter_->press()) { - if(false == layouter_->active_by_trace()) + if(false == layouter_->active_by_trace(arg)) layouter_->toolbox_answer(arg); layouter_->render(); API::dev::lazy_refresh(); From 592b904602238e7763f3dd04bb6d804aabe2327b Mon Sep 17 00:00:00 2001 From: besh81 Date: Thu, 29 Nov 2018 15:39:04 +0100 Subject: [PATCH 089/149] changed variable name int arg_tabbar* changed variable name int arg_tabbar* from pos to item_pos to avoid inconsistencies with arg_mouse.pos --- include/nana/gui/widgets/tabbar.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/nana/gui/widgets/tabbar.hpp b/include/nana/gui/widgets/tabbar.hpp index 093993f4..863b66ec 100644 --- a/include/nana/gui/widgets/tabbar.hpp +++ b/include/nana/gui/widgets/tabbar.hpp @@ -29,10 +29,10 @@ namespace nana { tabbar & widget; T & value; - std::size_t pos; ///< position of the item + std::size_t item_pos; ///< position of the item arg_tabbar(tabbar& wdg, T& v, std::size_t p) - : widget(wdg), value{ v }, pos(p) + : widget(wdg), value{ v }, item_pos(p) {} }; @@ -53,12 +53,12 @@ namespace nana : public arg_mouse { arg_tabbar_mouse(const arg_mouse& arg, tabbar& wdg, T& v, std::size_t p) - : arg_mouse{ arg }, widget(wdg), value{ v }, pos(p) + : arg_mouse{ arg }, widget(wdg), value{ v }, item_pos(p) {} tabbar & widget; T & value; - std::size_t pos; ///< position of the item + std::size_t item_pos; ///< position of the item }; template From 07a971c6eff7552b14cf5e02f566083f659e1167 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Fri, 30 Nov 2018 07:40:15 +0800 Subject: [PATCH 090/149] add support XDND for X --- include/nana/filesystem/filesystem.hpp | 24 +- include/nana/gui/dragdrop.hpp | 31 +- source/detail/platform_spec_posix.cpp | 161 +++++- source/detail/posix/msg_dispatcher.hpp | 15 +- source/detail/posix/msg_packet.hpp | 7 + source/detail/posix/platform_spec.hpp | 5 +- source/detail/posix/xdnd_protocol.hpp | 314 +++++++++++ source/gui/detail/bedrock_posix.cpp | 11 +- source/gui/dragdrop.cpp | 742 +++++++++++++++++-------- source/gui/widgets/treebox.cpp | 1 - 10 files changed, 1056 insertions(+), 255 deletions(-) create mode 100644 source/detail/posix/xdnd_protocol.hpp diff --git a/include/nana/filesystem/filesystem.hpp b/include/nana/filesystem/filesystem.hpp index 898fae3c..d5c0a7d9 100644 --- a/include/nana/filesystem/filesystem.hpp +++ b/include/nana/filesystem/filesystem.hpp @@ -103,12 +103,29 @@ namespace std { } // filesystem } // experimental + + namespace filesystem + { + using namespace experimental::filesystem; + } } // std #else # undef NANA_USING_STD_FILESYSTEM # define NANA_USING_STD_FILESYSTEM 1 -# include +# if ((defined(_MSC_VER) && (_MSC_VER >= 1912) && defined(_MSVC_LANG) && _MSVC_LANG >= 201703)) || \ + ((__cplusplus >= 201703L) && \ + (defined(__clang__) && (__clang_major__ >= 7) || \ + (!defined(__clang__) && defined(__GNUC__) && (__GNUC__ >= 8))) ) +# include +# else +# include + namespace std{ + namespace filesystem{ + using namespace std::experimental::filesystem; + } + } +# endif #endif @@ -508,8 +525,13 @@ namespace std { # endif } // filesystem } // experimental + + namespace filesystem { + using namespace std::experimental::filesystem; + } } // std + #endif //NANA_USING_NANA_FILESYSTEM #include diff --git a/include/nana/gui/dragdrop.hpp b/include/nana/gui/dragdrop.hpp index 96ed24ab..d426d34c 100644 --- a/include/nana/gui/dragdrop.hpp +++ b/include/nana/gui/dragdrop.hpp @@ -18,6 +18,7 @@ #include "basis.hpp" #include +#include namespace nana { @@ -41,22 +42,48 @@ namespace nana implementation* const impl_; }; + namespace detail + { + struct dragdrop_data; + } + class dragdrop { struct implementation; + /// Non-copyable dragdrop(const dragdrop&) = delete; dragdrop& operator=(const dragdrop&) = delete; + /// Non-movable dragdrop(dragdrop&&) = delete; dragdrop& operator=(dragdrop&&) = delete; public: + class data + { + friend struct dragdrop::implementation; + + /// Non-copyable + data(const data&) = delete; + data& operator=(const data&) = delete; + public: + data(); + data(data&&); + ~data(); + + data& operator=(data&& rhs); + + void insert(std::filesystem::path); + private: + detail::dragdrop_data* real_data_; + }; + dragdrop(window source); ~dragdrop(); void condition(std::function predicate_fn); - - void make_data(std::function generator); + void prepare_data(std::function generator); + void drop_finished(std::function finish_fn); private: implementation* const impl_; }; diff --git a/source/detail/platform_spec_posix.cpp b/source/detail/platform_spec_posix.cpp index f76b7266..f03f0b09 100644 --- a/source/detail/platform_spec_posix.cpp +++ b/source/detail/platform_spec_posix.cpp @@ -36,6 +36,8 @@ #include "posix/msg_dispatcher.hpp" +#include //debug + namespace nana { namespace detail @@ -500,11 +502,11 @@ namespace detail atombase_.net_wm_window_type_dialog = ::XInternAtom(display_, "_NET_WM_WINDOW_TYPE_DIALOG", False); atombase_.motif_wm_hints = ::XInternAtom(display_, "_MOTIF_WM_HINTS", False); - atombase_.clipboard = ::XInternAtom(display_, "CLIPBOARD", True); - atombase_.text = ::XInternAtom(display_, "TEXT", True); - atombase_.text_uri_list = ::XInternAtom(display_, "text/uri-list", True); - atombase_.utf8_string = ::XInternAtom(display_, "UTF8_STRING", True); - atombase_.targets = ::XInternAtom(display_, "TARGETS", True); + atombase_.clipboard = ::XInternAtom(display_, "CLIPBOARD", False); + atombase_.text = ::XInternAtom(display_, "TEXT", False); + atombase_.text_uri_list = ::XInternAtom(display_, "text/uri-list", False); + atombase_.utf8_string = ::XInternAtom(display_, "UTF8_STRING", False); + atombase_.targets = ::XInternAtom(display_, "TARGETS", False); atombase_.xdnd_aware = ::XInternAtom(display_, "XdndAware", False); atombase_.xdnd_enter = ::XInternAtom(display_, "XdndEnter", False); @@ -514,6 +516,7 @@ namespace detail atombase_.xdnd_drop = ::XInternAtom(display_, "XdndDrop", False); atombase_.xdnd_selection = ::XInternAtom(display_, "XdndSelection", False); atombase_.xdnd_typelist = ::XInternAtom(display_, "XdndTypeList", False); + atombase_.xdnd_leave = ::XInternAtom(display_, "XdndLeave", False); atombase_.xdnd_finished = ::XInternAtom(display_, "XdndFinished", False); msg_dispatcher_ = new msg_dispatcher(display_); @@ -1041,7 +1044,7 @@ namespace detail msg_dispatcher_->dispatch(reinterpret_cast(modal)); } - void platform_spec::msg_dispatch(std::function msg_filter_fn) + void platform_spec::msg_dispatch(std::function msg_filter_fn) { msg_dispatcher_->dispatch(msg_filter_fn); @@ -1116,15 +1119,49 @@ namespace detail platform_scope_guard lock; if(0 != xdnd_.dragdrop.count(wd)) return false; - +#if 0 //deprecated + std::cout<<"ChangeProperty XdndAware"<(wd), atombase_.xdnd_aware, XA_ATOM, sizeof(int) * 8, PropModeReplace, reinterpret_cast(&dndver), 1); - +#endif xdnd_.dragdrop[wd] = ddrop; return true; } + std::size_t platform_spec::dragdrop_target(native_window_type wd, bool insert, std::size_t count) + { + std::size_t new_val = 0; + platform_scope_guard lock; + if(insert) + { + new_val = (xdnd_.targets[wd] += count); + if(1 == new_val) + { + std::cout<<"ChangeProperty XdndAware"<(wd), atombase_.xdnd_aware, XA_ATOM, sizeof(int) * 8, + PropModeReplace, reinterpret_cast(&dndver), 1); + } + } + else + { + auto i = xdnd_.targets.find(wd); + if(i == xdnd_.targets.end()) + return 0; + + new_val = (i->second > count ? i->second - count : 0); + if(0 == new_val) + { + xdnd_.targets.erase(wd); + ::XDeleteProperty(display_, reinterpret_cast(wd), atombase_.xdnd_aware); + } + else + i->second = new_val; + } + return new_val; + } + x11_dragdrop_interface* platform_spec::remove_dragdrop(native_window_type wd) { platform_scope_guard lock; @@ -1196,6 +1233,7 @@ namespace detail } else if(evt.xselection.property == self.atombase_.xdnd_selection) { + std::cout<<"Platform SelectionNotfy by XdndSelection, type="<push_back(file); } + + std::cout<<" file size:"<size()<size()) { msg.u.mouse_drop.window = evt.xselection.requestor; @@ -1241,9 +1283,12 @@ namespace detail accepted = true; ::XFree(data); } + else + std::cout<<" invalid property"<(&(evt.xclient.data.l[2])); unsigned long len = 3; unsigned char * data = 0; @@ -1322,54 +1378,119 @@ namespace detail Atom type; int format; unsigned long bytes_left; - ::XGetWindowProperty(self.display_, self.xdnd_.wd_src, self.atombase_.xdnd_typelist, 0, 0, False, + auto status = ::XGetWindowProperty(self.display_, self.xdnd_.wd_src, self.atombase_.xdnd_typelist, 0, 0, False, XA_ATOM, &type, &format, &len, &bytes_left, &data); + std::cout<<", XGetWindowProperty xdnd_typelist = "<> 16); int y = (evt.xclient.data.l[2] & 0xFFFF); - bool accepted = false; + int accepted = 0; //0 means refusing, 1 means accpeting //We have got the type what we want. if(self.xdnd_.good_type != None) { Window child; ::XTranslateCoordinates(self.display_, self.root_window(), evt.xclient.window, x, y, &self.xdnd_.pos.x, &self.xdnd_.pos.y, &child); + std::cout<<" find_window"<(evt.xclient.window), self.xdnd_.pos); if(wd && wd->flags.dropable) { - accepted = true; + //Cache the time stamp in XdndPosition, and the time stamp must be passed to XConvertSelection for requesting selection self.xdnd_.timestamp = evt.xclient.data.l[3]; self.xdnd_.pos = wd->pos_root; + accepted = 1; } + + std::cout<<" return,"< #include +#include //debug + namespace nana { namespace detail @@ -190,8 +192,15 @@ namespace detail } else { - if(msg_filter_fn(msg)) + switch(msg_filter_fn(msg)) + { + case propagation_chain::exit: return; + case propagation_chain::stop: + break; + case propagation_chain::pass: + proc_.event_proc(display_, msg); + } } } } @@ -222,6 +231,10 @@ namespace detail if(keymap[event.xkey.keycode / 8] & (1 << (event.xkey.keycode % 8))) continue; } + else if(SelectionRequest == event.type) + { + std::cout<<"Dispatcher SelectionRequest"<); + void msg_dispatch(std::function); //X Selections void* request_selection(native_window_type requester, Atom type, size_t & bufsize); @@ -268,6 +269,7 @@ namespace detail const nana::paint::graphics& keep_window_icon(native_window_type, const nana::paint::image&); bool register_dragdrop(native_window_type, x11_dragdrop_interface*); + std::size_t dragdrop_target(native_window_type, bool insert, std::size_t count); x11_dragdrop_interface* remove_dragdrop(native_window_type); private: static int _m_msg_filter(XEvent&, msg_packet_tag&); @@ -327,6 +329,7 @@ namespace detail nana::point pos; std::map dragdrop; + std::map targets; }xdnd_; msg_dispatcher * msg_dispatcher_; diff --git a/source/detail/posix/xdnd_protocol.hpp b/source/detail/posix/xdnd_protocol.hpp new file mode 100644 index 00000000..d902d175 --- /dev/null +++ b/source/detail/posix/xdnd_protocol.hpp @@ -0,0 +1,314 @@ +/* + * X-Window XDND Protocol Implementation + * Nana C++ Library(http://www.nanapro.org) + * Copyright(C) 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/detail/posix/xdnd_protocol.hpp + * + * The XDS is not supported. + */ +#ifndef NANA_DETAIL_POSIX_XDND_PROTOCOL_INCLUDED +#define NANA_DETAIL_POSIX_XDND_PROTOCOL_INCLUDED + +#include "platform_spec.hpp" +#include +#include + + +#include //debug + +namespace nana{ + namespace detail + { + + struct xdnd_data + { + std::vector files; + }; + + class xdnd_protocol + { + public: + enum class xdnd_status_state + { + normal, + position_sent, + status_ignore + }; + + xdnd_protocol(Window source): + spec_(nana::detail::platform_spec::instance()), + source_(source) + { + detail::platform_scope_guard lock; + ::XSetSelectionOwner(spec_.open_display(), spec_.atombase().xdnd_selection, source, CurrentTime); + std::cout<<"XSetSelectionOwner "<(xclient.data.l[0]); + bool is_accepted_by_target = (xclient.data.l[1] & 1); + + std::cout<<"XdndStatus: Accepted="<(xclient.data.l[2] >> 16), + static_cast(xclient.data.l[2] & 0xFFFF), + static_cast(xclient.data.l[3] >> 16), + static_cast(xclient.data.l[3] & 0xFFFF) + }; + + if(!rct.empty()) + { + mvout_table_[target_wd] = rct; + std::cout<<". rct=("< mvout_table_; + }; //end class xdnd_protocol + } +} + +#endif \ No newline at end of file diff --git a/source/gui/detail/bedrock_posix.cpp b/source/gui/detail/bedrock_posix.cpp index cbee18d2..9beb3bab 100644 --- a/source/gui/detail/bedrock_posix.cpp +++ b/source/gui/detail/bedrock_posix.cpp @@ -21,6 +21,7 @@ #include "inner_fwd_implement.hpp" #include #include +#include //debug namespace nana { @@ -443,6 +444,8 @@ namespace detail { case nana::detail::msg_packet_tag::pkt_family::mouse_drop: msgwd = brock.wd_manager().find_window(native_window, {msg.u.mouse_drop.x, msg.u.mouse_drop.y}); + + std::cout<<" MouseDrop msgwd="<flags.enabled && (atoms.wm_delete_window == static_cast(xevent.xclient.data.l[0]))) + if(msgwnd->flags.enabled && (atoms.wm_delete_window == static_cast(xclient.data.l[0]))) { arg_unload arg; arg.window_handle = reinterpret_cast(msgwnd); diff --git a/source/gui/dragdrop.cpp b/source/gui/dragdrop.cpp index e6d653a3..baa4b8f3 100644 --- a/source/gui/dragdrop.cpp +++ b/source/gui/dragdrop.cpp @@ -26,23 +26,52 @@ # include # include #elif defined(NANA_X11) -# include "../detail/posix/platform_spec.hpp" +# include "../detail/posix/xdnd_protocol.hpp" # include # include # include #endif - namespace nana { + namespace detail + { + struct dragdrop_data + { + std::vector files; + }; + +#ifdef NANA_X11 + xdnd_data to_xdnd_data(const dragdrop_data& data) + { + xdnd_data xdata; + xdata.files = data.files; + return xdata; + } +#endif + } + + class dragdrop_session { public: + struct target_rep + { + std::set target; + std::map native_target_count; + }; + virtual ~dragdrop_session() = default; void insert(window source, window target) { - table_[source].insert(target); + auto &rep = table_[source]; + rep.target.insert(target); +#ifdef NANA_X11 + auto native_wd = API::root(target); + rep.native_target_count[native_wd] += 1; + nana::detail::platform_spec::instance().dragdrop_target(native_wd, true, 1); +#endif } void erase(window source, window target) @@ -51,17 +80,36 @@ namespace nana if (table_.end() == i) return; - i->second.erase(target); + i->second.target.erase(target); - if ((nullptr == target) || i->second.empty()) +#ifdef NANA_WINDOWS + if ((nullptr == target) || i->second.target.empty()) table_.erase(i); +#else + if(nullptr == target) + { + //remove all targets of source + for(auto & native : i->second.native_target_count) + { + nana::detail::platform_spec::instance().dragdrop_target(native.first, false, native.second); + } + + table_.erase(i); + } + else + { + nana::detail::platform_spec::instance().dragdrop_target(API::root(target), false, 1); + if(i->second.target.empty()) + table_.erase(i); + } +#endif } bool has(window source, window target) const { auto i = table_.find(source); if (i != table_.end()) - return (0 != i->second.count(target)); + return (0 != i->second.target.count(target)); return false; } @@ -89,7 +137,7 @@ namespace nana return current_source_; } private: - std::map> table_; + std::map table_; window current_source_{ nullptr }; }; @@ -128,6 +176,12 @@ namespace nana class win32com_drop_target : public IDropTarget, public dragdrop_session { public: + win32com_drop_target(bool simple_mode): + simple_mode_(simple_mode) + { + + } + //Implements IUnknown STDMETHODIMP QueryInterface(REFIID riid, void **ppv) { @@ -154,26 +208,44 @@ namespace nana private: // IDropTarget - STDMETHODIMP DragEnter(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) + STDMETHODIMP DragEnter(IDataObject* data, DWORD grfKeyState, POINTL pt, DWORD* req_effect) { - *pdwEffect &= DROPEFFECT_COPY; + *req_effect &= DROPEFFECT_COPY; + + FORMATETC fmt = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; + STGMEDIUM medium; + if (S_OK == data->GetData(&fmt, &medium)) + { + ::ReleaseStgMedium(&medium); + effect_ = DROPEFFECT_COPY; + } + return S_OK; } - STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) + STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, DWORD* req_effect) { - auto hovered_wd = API::find_window(point(pt.x, pt.y)); + //bool found_data = false; + if (simple_mode_) + { + auto hovered_wd = API::find_window(point(pt.x, pt.y)); - if ((hovered_wd && (hovered_wd == this->current_source())) || this->has(this->current_source(), hovered_wd)) - *pdwEffect &= DROPEFFECT_COPY; + if ((hovered_wd && (hovered_wd == this->current_source())) || this->has(this->current_source(), hovered_wd)) + *req_effect &= DROPEFFECT_COPY; + else + *req_effect = DROPEFFECT_NONE; + } else - *pdwEffect = DROPEFFECT_NONE; + { + *req_effect = effect_; + } return S_OK; } STDMETHODIMP DragLeave() { - return E_NOTIMPL; + effect_ = DROPEFFECT_NONE; + return S_OK; } STDMETHODIMP Drop(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) @@ -182,6 +254,8 @@ namespace nana } private: LONG ref_count_{ 1 }; + bool const simple_mode_; //Simple mode behaves the simple_dragdrop. + DWORD effect_{ DROPEFFECT_NONE }; }; class drop_source : public win32com_iunknown @@ -217,41 +291,170 @@ namespace nana window const window_handle_; }; - class drop_data : public win32com_iunknown + class win32_dropdata : public win32com_iunknown { - struct medium +#if 0 //deprecated + class enumer : public win32com_iunknown { - STGMEDIUM * stgmedium; - FORMATETC * format; - }; - public: - ~drop_data() - { - if(hglobal_) - ::GlobalFree(hglobal_); - } + public: + enumer(drop_data& data) : + data_(data) + {} - void assign(const std::vector& files) - { - std::size_t bytes = 0; - for (auto & f : files) - bytes += (f.size() + 1) * sizeof(f.front()); - - hglobal_ = ::GlobalAlloc(GHND | GMEM_SHARE, sizeof(DROPFILES) + bytes); - - auto dropfiles = reinterpret_cast(::GlobalLock(hglobal_)); - dropfiles->pFiles = sizeof(DROPFILES); - dropfiles->fWide = true; - - auto file_buf = reinterpret_cast(dropfiles) + sizeof(DROPFILES); - - for (auto & f : files) + enumer(const enumer& rhs): + data_(rhs.data_), + cursor_(rhs.cursor_) { - std::memcpy(file_buf, f.data(), (f.size() + 1) * sizeof(f.front())); - file_buf += f.size() + 1; + } + private: + // Implement IEnumFORMATETC + HRESULT Clone(IEnumFORMATETC **ppenum) override + { + *ppenum = new enumer{ *this }; + return S_OK; } - ::GlobalUnlock(hglobal_); + HRESULT Next(ULONG celt, FORMATETC *rgelt, ULONG *pceltFetched) override + { + HRESULT result = (cursor_ + celt <= data_.entries_.size() ? S_OK : S_FALSE); + + auto const fetched = (std::min)(std::size_t(celt), data_.entries_.size() - cursor_); + + for (std::size_t i = 0; i < fetched; ++i) + *rgelt++ = data_.entries_[cursor_++]->format; + + if (pceltFetched) + *pceltFetched = static_cast(fetched); + else if (celt > 1) + return S_FALSE; + + return (celt == fetched ? S_OK : S_FALSE); + } + + HRESULT Reset() override + { + cursor_ = 0; + return S_OK; + } + + HRESULT Skip(ULONG celt) override + { + if (cursor_ + celt < data_.entries_.size()) + { + cursor_ += celt; + return S_OK; + } + + cursor_ = data_.entries_.size(); + return S_FALSE; + } + private: + drop_data & data_; + std::size_t cursor_; + }; +#endif + public: + struct data_entry + { + FORMATETC format; + STGMEDIUM medium; + bool read_from; //Indicates the data which is used for reading. + + ~data_entry() + { + ::CoTaskMemFree(format.ptd); + ::ReleaseStgMedium(&medium); + } + + bool compare(const FORMATETC& fmt, bool rdfrom) const + { + return (format.cfFormat == fmt.cfFormat && + (format.tymed & fmt.tymed) != 0 && + (format.dwAspect == DVASPECT_THUMBNAIL || format.dwAspect == DVASPECT_ICON || medium.tymed == TYMED_NULL || format.lindex == fmt.lindex || (format.lindex == 0 && fmt.lindex == -1) || (format.lindex == -1 && fmt.lindex == 0)) && + format.dwAspect == fmt.dwAspect && read_from == rdfrom); + } + }; + + data_entry * find(const FORMATETC& fmt, bool read_from) const + { + data_entry * last_weak_match = nullptr; + + for (auto & entry : entries_) + { + if (entry->compare(fmt, read_from)) + { + auto entry_ptd = entry->format.ptd; + if (entry_ptd && fmt.ptd && entry_ptd->tdSize == fmt.ptd->tdSize && (0 == std::memcmp(entry_ptd, fmt.ptd, fmt.ptd->tdSize))) + return entry.get(); + else if (nullptr == entry_ptd && nullptr == fmt.ptd) + return entry.get(); + + last_weak_match = entry.get(); + } + } + return last_weak_match; + } + + void assign(const detail::dragdrop_data& data) + { + if (!data.files.empty()) + { + std::size_t bytes = sizeof(wchar_t); + for (auto & file : data.files) + { + auto file_s = file.wstring(); + bytes += (file_s.size() + 1) * sizeof(file_s.front()); + } + + auto hglobal = ::GlobalAlloc(GHND | GMEM_SHARE, sizeof(DROPFILES) + bytes); + + auto dropfiles = reinterpret_cast(::GlobalLock(hglobal)); + dropfiles->pFiles = sizeof(DROPFILES); + dropfiles->fWide = true; + + auto file_buf = reinterpret_cast(dropfiles) + sizeof(DROPFILES); + + for (auto & file : data.files) + { + auto file_s = file.wstring(); + std::memcpy(file_buf, file_s.data(), (file_s.size() + 1) * sizeof(file_s.front())); + file_buf += (file_s.size() + 1) * sizeof(file_s.front()); + } + *reinterpret_cast(file_buf) = 0; + + ::GlobalUnlock(hglobal); + + assign(hglobal); + } + } + + data_entry* assign(HGLOBAL hglobal) + { + FORMATETC fmt = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; + auto entry = find(fmt, true); + if (entry) + { + //Free the current entry for reuse + ::CoTaskMemFree(entry->format.ptd); + ::ReleaseStgMedium(&entry->medium); + } + else + { + //Create a new entry + entries_.emplace_back(new data_entry); + entry = entries_.back().get(); + } + + //Assign the format to the entry. + entry->read_from = true; + entry->format = fmt; + + //Assign the stgMedium + entry->medium.tymed = TYMED_HGLOBAL; + entry->medium.hGlobal = hglobal; + entry->medium.pUnkForRelease = nullptr; + + return entry; } public: // Implement IDataObject @@ -262,15 +465,9 @@ namespace nana pmedium->hGlobal = nullptr; - for (auto & med : mediums_) - { - if ((request_format->tymed & med.format->tymed) && - (request_format->dwAspect == med.format->dwAspect) && - (request_format->cfFormat == med.format->cfFormat)) - { - return _m_copy_medium(pmedium, med.stgmedium, med.format); - } - } + auto entry = find(*request_format, true); + if (entry) + return _m_copy_medium(pmedium, &entry->medium, &entry->format); return DV_E_FORMATETC; } @@ -290,13 +487,13 @@ namespace nana HRESULT result = DV_E_TYMED; - for(auto & med : mediums_) + for (auto & entry : entries_) { - if (med.format->tymed & pformatetc->tymed) + if (entry->format.tymed & pformatetc->tymed) { - if (med.format->cfFormat == pformatetc->cfFormat) + if (entry->format.cfFormat == pformatetc->cfFormat) return S_OK; - + result = DV_E_FORMATETC; } } @@ -316,26 +513,16 @@ namespace nana if (pformatetc->tymed != pmedium->tymed) return E_FAIL; - medium retain; - retain.format = new FORMATETC; - retain.stgmedium = new (std::nothrow) STGMEDIUM; - if (nullptr == retain.stgmedium) - { - delete retain.format; - return E_FAIL; - } + entries_.emplace_back(new data_entry); + auto entry = entries_.back().get(); - std::memset(retain.format, 0, sizeof(FORMATETC)); - std::memset(retain.stgmedium, 0, sizeof(STGMEDIUM)); + entry->format = *pformatetc; - *retain.format = *pformatetc; - - _m_copy_medium(retain.stgmedium, pmedium, pformatetc); + _m_copy_medium(&entry->medium, pmedium, pformatetc); if (TRUE == fRelease) ::ReleaseStgMedium(pmedium); - mediums_.emplace_back(retain); return S_OK; } @@ -347,11 +534,12 @@ namespace nana if (DATADIR_GET != dwDirection) return E_NOTIMPL; - *ppenumFormatEtc = NULL; + *ppenumFormatEtc = nullptr; FORMATETC rgfmtetc[] = { - { CF_UNICODETEXT, NULL, DVASPECT_CONTENT, 0, TYMED_HGLOBAL }, + //{ CF_UNICODETEXT, nullptr, DVASPECT_CONTENT, 0, TYMED_HGLOBAL } + { CF_HDROP, nullptr, DVASPECT_CONTENT, 0, TYMED_HGLOBAL } }; return ::SHCreateStdEnumFmtEtc(ARRAYSIZE(rgfmtetc), rgfmtetc, ppenumFormatEtc); } @@ -382,14 +570,16 @@ namespace nana stgmed_dst->hGlobal = (HGLOBAL)OleDuplicateData(stgmed_src->hGlobal, fmt_src->cfFormat, 0); break; case TYMED_GDI: - stgmed_dst->hBitmap = (HBITMAP)OleDuplicateData(stgmed_src->hBitmap, fmt_src->cfFormat, 0); + case TYMED_ENHMF: + //GDI object can't be copied to an existing HANDLE + if (stgmed_dst->hGlobal) + return E_INVALIDARG; + + stgmed_dst->hGlobal = (HBITMAP)OleDuplicateData(stgmed_src->hGlobal, fmt_src->cfFormat, 0); break; case TYMED_MFPICT: stgmed_dst->hMetaFilePict = (HMETAFILEPICT)OleDuplicateData(stgmed_src->hMetaFilePict, fmt_src->cfFormat, 0); break; - case TYMED_ENHMF: - stgmed_dst->hEnhMetaFile = (HENHMETAFILE)OleDuplicateData(stgmed_src->hEnhMetaFile, fmt_src->cfFormat, 0); - break; case TYMED_FILE: stgmed_dst->lpszFileName = (LPOLESTR)OleDuplicateData(stgmed_src->lpszFileName, fmt_src->cfFormat, 0); break; @@ -415,14 +605,41 @@ namespace nana return S_OK; } private: - HGLOBAL hglobal_{nullptr}; - std::vector mediums_; + std::vector> entries_; }; #elif defined(NANA_X11) + constexpr int xdnd_version = 5; + + class x11_dropdata + { + public: + void assign(const detail::dragdrop_data& data) + { + data_ = &data; + } + + const detail::dragdrop_data* data() const + { + return data_; + } + private: + const detail::dragdrop_data* data_{nullptr}; + }; + class x11_dragdrop: public detail::x11_dragdrop_interface, public dragdrop_session { + public: + x11_dragdrop(bool simple_mode): + simple_mode_(simple_mode) + { + } + + bool simple_mode() const noexcept + { + return simple_mode_; + } public: //Implement x11_dragdrop_interface void add_ref() override @@ -439,6 +656,7 @@ namespace nana return val; } private: + bool const simple_mode_; std::atomic ref_count_{ 1 }; }; @@ -497,9 +715,11 @@ namespace nana dragdrop_service() = default; public: #ifdef NANA_WINDOWS - using dragdrop_target = win32com_drop_target; + using dragdrop_target = win32com_drop_target; + using dropdata_type = win32_dropdata; #else - using dragdrop_target = x11_dragdrop; + using dragdrop_target = x11_dragdrop; + using dropdata_type = x11_dropdata; #endif static dragdrop_service& instance() @@ -508,7 +728,7 @@ namespace nana return serv; } - dragdrop_session* create_dragdrop(window wd) + dragdrop_session* create_dragdrop(window wd, bool simple_mode) { auto native_wd = API::root(wd); if (nullptr == native_wd) @@ -519,7 +739,7 @@ namespace nana auto i = table_.find(native_wd); if(table_.end() == i) { - ddrop = new dragdrop_target; + ddrop = new dragdrop_target{simple_mode}; #ifdef NANA_WINDOWS if (table_.empty()) ::OleInitialize(nullptr); @@ -572,48 +792,45 @@ namespace nana } } - bool dragdrop(window drag_wd) + bool dragdrop(window drag_wd, dropdata_type* dropdata) { auto i = table_.find(API::root(drag_wd)); - if (table_.end() == i) + if ((!dropdata) && table_.end() == i) return false; + internal_revert_guard rvt_lock; + #ifdef NANA_WINDOWS auto drop_src = new drop_source{ drag_wd }; - auto drop_dat = new (std::nothrow) drop_data; - if (!drop_dat) - { - delete drop_src; - return false; - } i->second->set_current_source(drag_wd); - DWORD eff; - auto status = ::DoDragDrop(drop_dat, drop_src, DROPEFFECT_COPY, &eff); + DWORD result_effect{ DROPEFFECT_NONE }; + auto status = ::DoDragDrop(dropdata, drop_src, DROPEFFECT_COPY, &result_effect); i->second->set_current_source(nullptr); delete drop_src; - delete drop_dat; - return true; + return (DROPEFFECT_NONE != result_effect); #elif defined(NANA_X11) - auto ddrop = dynamic_cast(i->second); + auto& atombase = _m_spec().atombase(); + + auto ddrop = dynamic_cast(i->second); auto const native_source = reinterpret_cast(API::root(drag_wd)); { detail::platform_scope_guard lock; - ::XSetSelectionOwner(_m_spec().open_display(), _m_spec().atombase().xdnd_selection, native_source, CurrentTime); + ::XSetSelectionOwner(_m_spec().open_display(), atombase.xdnd_selection, native_source, CurrentTime); } hovered_.window_handle = nullptr; hovered_.native_wd = 0; window target_wd = 0; - auto& atombase = _m_spec().atombase(); - //while(true) + + if(ddrop->simple_mode()) { _m_spec().msg_dispatch([this, ddrop, drag_wd, native_source, &target_wd, &atombase](const detail::msg_packet_tag& msg_pkt) mutable{ @@ -624,7 +841,8 @@ namespace nana { auto pos = API::cursor_position(); auto native_cur_wd = reinterpret_cast(detail::native_interface::find_window(pos.x, pos.y)); - +#if 0 + const char* icon = nullptr; if(hovered_.native_wd != native_cur_wd) { if(hovered_.native_wd) @@ -633,36 +851,126 @@ namespace nana ::XUndefineCursor(disp, hovered_.native_wd); } - _m_client_msg(native_cur_wd, native_source, atombase.xdnd_enter, atombase.text_uri_list, XA_STRING); + _m_client_msg(native_cur_wd, native_source, 1, atombase.xdnd_enter, atombase.text_uri_list, XA_STRING); + hovered_.native_wd = native_cur_wd; + + if(!ddrop->simple_mode()) + icon = "dnd-move"; + } + + if(ddrop->simple_mode()) + { + auto cur_wd = API::find_window(API::cursor_position()); + if(hovered_.window_handle != cur_wd) + { + hovered_.window_handle = cur_wd; + + icon = (((drag_wd == cur_wd) || ddrop->has(drag_wd, cur_wd)) ? "dnd-move" : "dnd-none"); + } + } + +#else + const char* icon = nullptr; + if(hovered_.native_wd != native_cur_wd) + { + if(hovered_.native_wd) + { + _m_free_cursor(); + ::XUndefineCursor(disp, hovered_.native_wd); + } + + _m_client_msg(native_cur_wd, native_source, 1, atombase.xdnd_enter, atombase.text_uri_list, XA_STRING); hovered_.native_wd = native_cur_wd; } + if(ddrop->simple_mode()) + { + auto cur_wd = API::find_window(API::cursor_position()); + std::cout<<" Hovered="<has(drag_wd, cur_wd)) ? "dnd-move" : "dnd-none"); + std::cout<<" ICON="<has(drag_wd, cur_wd)) ? "dnd-move" : "dnd-none"); hovered_.cursor = ::XcursorFilenameLoadCursor(disp, icons_.cursor(icon).c_str()); - ::XDefineCursor(disp, native_cur_wd, hovered_.cursor); - } + ::XDefineCursor(disp, native_cur_wd, hovered_.cursor); + } +#endif } else if(msg_pkt.u.xevent.type == ButtonRelease) { target_wd = API::find_window(API::cursor_position()); ::XUndefineCursor(disp, hovered_.native_wd); _m_free_cursor(); - return true; + API::release_capture(drag_wd); + return detail::propagation_chain::exit; } } - return false; + return detail::propagation_chain::stop; }); } + else + { + auto data = detail::to_xdnd_data(*dropdata->data()); + + API::set_capture(drag_wd, true); + nana::detail::xdnd_protocol xdnd_proto{native_source}; + + //Not simple mode + _m_spec().msg_dispatch([this, ddrop, &data, drag_wd, xdnd_proto, native_source, &target_wd, &atombase](const detail::msg_packet_tag& msg_pkt) mutable{ + if(detail::msg_packet_tag::pkt_family::xevent == msg_pkt.kind) + { + auto const disp = _m_spec().open_display(); + if (MotionNotify == msg_pkt.u.xevent.type) + { + auto pos = API::cursor_position(); + auto native_cur_wd = reinterpret_cast(detail::native_interface::find_window(pos.x, pos.y)); + + xdnd_proto.mouse_move(native_cur_wd, pos); + } + else if(ClientMessage == msg_pkt.u.xevent.type) + { + auto & xclient = msg_pkt.u.xevent.xclient; + if(xdnd_proto.client_message(xclient)) + { + API::release_capture(drag_wd); + return detail::propagation_chain::exit; + } + } + else if(SelectionRequest == msg_pkt.u.xevent.type) + { + xdnd_proto.selection_request(msg_pkt.u.xevent.xselectionrequest, data); + } + else if(msg_pkt.u.xevent.type == ButtonRelease) + { + std::cout<<"ButtonRelease"<ddrop = dragdrop_service::instance().create_dragdrop(drag_wd); + impl_->ddrop = dragdrop_service::instance().create_dragdrop(drag_wd, true); impl_->window_handle = drag_wd; API::dev::window_draggable(drag_wd, true); auto & events = API::events<>(drag_wd); -#if 1 //#ifdef NANA_WINDOWS impl_->events.destroy = events.destroy.connect_unignorable([this](const arg_destroy&) { dragdrop_service::instance().remove(impl_->window_handle); API::dev::window_draggable(impl_->window_handle, false); @@ -825,7 +1116,9 @@ namespace nana auto real_wd = reinterpret_cast<::nana::detail::basic_window*>(arg.window_handle); real_wd->other.dnd_state = dragdrop_status::in_progress; - auto has_dropped = dragdrop_service::instance().dragdrop(arg.window_handle); + std::unique_ptr dropdata{new dragdrop_service::dropdata_type}; + + auto has_dropped = dragdrop_service::instance().dragdrop(arg.window_handle, dropdata.get()); real_wd->other.dnd_state = dragdrop_status::not_ready; impl_->dragging = false; @@ -838,65 +1131,6 @@ namespace nana i->second(); } }); -#elif 1 - events.mouse_down.connect_unignorable([drag_wd](const arg_mouse& arg){ - if (arg.is_left_button() && API::is_window(drag_wd)) - { - //API::set_capture(drag_wd, true); - - using basic_window = ::nana::detail::basic_window; - auto real_wd = reinterpret_cast<::nana::detail::basic_window*>(drag_wd); - real_wd->other.dnd_state = dragdrop_status::ready; - } - }); - - events.mouse_move.connect_unignorable([this](const arg_mouse& arg){ - if (!arg.is_left_button()) - return; - - if (impl_->dragging) - { - auto drop_wd = API::find_window(API::cursor_position()); - auto i = impl_->targets.find(drop_wd); - - } - else - { - if ((!impl_->predicate) || impl_->predicate()) - { - if (API::is_window(arg.window_handle)) - { - impl_->dragging = true; - using basic_window = ::nana::detail::basic_window; - auto real_wd = reinterpret_cast<::nana::detail::basic_window*>(arg.window_handle); - real_wd->other.dnd_state = dragdrop_status::in_progress; - - dragdrop_service::instance().dragdrop(arg.window_handle); - return; - } - } - - //API::release_capture(impl_->window_handle); - } - - }); - - events.mouse_up.connect_unignorable([this]{ - if (impl_->cancel()) - { - auto drop_wd = API::find_window(API::cursor_position()); - auto i = impl_->targets.find(drop_wd); - if (impl_->targets.end() == i || !i->second) - return; - - i->second(); - } - }); - - events.key_press.connect_unignorable([this]{ - impl_->cancel(); - }); -#endif } simple_dragdrop::~simple_dragdrop() @@ -931,21 +1165,15 @@ namespace nana impl_->targets[target].swap(drop_fn); } - - - - - - //This is class dragdrop struct dragdrop::implementation { - window source_handle; + window const source_handle; bool dragging{ false }; + dragdrop_session * ddrop{nullptr}; std::function predicate; - - std::function generator; - + std::function generator; + std::function drop_finished; struct event_handlers { @@ -954,44 +1182,43 @@ namespace nana nana::event_handle mouse_down; }events; - void make_data() + implementation(window source): + source_handle(source) { - //https://www.codeproject.com/Articles/840/How-to-Implement-Drag-and-Drop-Between-Your-Progra + ddrop = dragdrop_service::instance().create_dragdrop(source, false); + API::dev::window_draggable(source, true); + } - std::vector files; - files.push_back(L"D:\\universal_access"); - files.push_back(L"D:\\н¨Îı¾Îĵµ.cpp"); + void make_drop() + { + auto transf_data = generator(); + dragdrop_service::dropdata_type dropdata; + dropdata.assign(*transf_data.real_data_); +/* //deprecated +#ifdef NANA_WINDOWS + drop_source drop_src{ source_handle }; + DWORD result_effect = DROPEFFECT_NONE; + auto status = ::DoDragDrop(&dropdata, &drop_src, DROPEFFECT_COPY | DROPEFFECT_MOVE, &result_effect); - std::size_t bytes = 0; - for (auto & f : files) - bytes += (f.size() + 1) * sizeof(f.front()); - - auto data_handle = ::GlobalAlloc(GHND | GMEM_SHARE, sizeof(DROPFILES) + bytes); - - auto dropfiles = reinterpret_cast(::GlobalLock(data_handle)); - dropfiles->pFiles = sizeof(DROPFILES); - dropfiles->fWide = true; - - auto file_buf = reinterpret_cast(dropfiles) + sizeof(DROPFILES); - - for (auto & f : files) + if (DROPEFFECT_NONE == result_effect) { - std::memcpy(file_buf, f.data(), (f.size() + 1) * sizeof(f.front())); - file_buf += f.size() + 1; } - ::GlobalUnlock(data_handle); + if (drop_finished) + drop_finished(DROPEFFECT_NONE != result_effect); +#else +#endif +*/ + auto has_dropped = dragdrop_service::instance().dragdrop(source_handle, &dropdata); - - generator(); + if(drop_finished) + drop_finished(has_dropped); } }; dragdrop::dragdrop(window source) : - impl_(new implementation) + impl_(new implementation(source)) { - impl_->source_handle = source; - auto & events = API::events(source); impl_->events.destroy = events.destroy.connect_unignorable([this](const arg_destroy&) { dragdrop_service::instance().remove(impl_->source_handle); @@ -1017,13 +1244,15 @@ namespace nana auto real_wd = reinterpret_cast<::nana::detail::basic_window*>(arg.window_handle); real_wd->other.dnd_state = dragdrop_status::in_progress; - impl_->make_data(); + impl_->make_drop(); - auto has_dropped = dragdrop_service::instance().dragdrop(arg.window_handle); + //deprecated + //auto has_dropped = dragdrop_service::instance().dragdrop(arg.window_handle); real_wd->other.dnd_state = dragdrop_status::not_ready; impl_->dragging = false; + /* //deprecated if (has_dropped) { auto drop_wd = API::find_window(API::cursor_position()); @@ -1031,21 +1260,70 @@ namespace nana //if ((impl_->targets.end() != i) && i->second) // i->second(); } + */ }); } dragdrop::~dragdrop() { + if (impl_->source_handle) + { + dragdrop_service::instance().remove(impl_->source_handle); + API::dev::window_draggable(impl_->source_handle, false); + + API::umake_event(impl_->events.destroy); + API::umake_event(impl_->events.mouse_down); + API::umake_event(impl_->events.mouse_move); + } delete impl_; } void dragdrop::condition(std::function predicate_fn) { - + impl_->predicate = predicate_fn; } - void dragdrop::make_data(std::function generator) + void dragdrop::prepare_data(std::function generator) { impl_->generator = generator; } + + void dragdrop::drop_finished(std::function finish_fn) + { + impl_->drop_finished = finish_fn; + } + + + dragdrop::data::data(): + real_data_(new detail::dragdrop_data) + { + } + + dragdrop::data::~data() + { + delete real_data_; + } + + dragdrop::data::data(data&& rhs): + real_data_(new detail::dragdrop_data) + { + std::swap(real_data_, rhs.real_data_); + } + + dragdrop::data& dragdrop::data::operator=(data&& rhs) + { + if (this != &rhs) + { + auto moved_data = new detail::dragdrop_data; + delete real_data_; + real_data_ = rhs.real_data_; + rhs.real_data_ = moved_data; + } + return *this; + } + + void dragdrop::data::insert(std::filesystem::path path) + { + real_data_->files.emplace_back(std::move(path)); + } }//end namespace nana \ No newline at end of file diff --git a/source/gui/widgets/treebox.cpp b/source/gui/widgets/treebox.cpp index fa8a9ad0..baef8643 100644 --- a/source/gui/widgets/treebox.cpp +++ b/source/gui/widgets/treebox.cpp @@ -609,7 +609,6 @@ namespace nana auto last_pos = tree.distance_if(last(true), pred_allow_child{}); auto const capacity = screen_capacity(true); - auto const item_px = data.comp_placer->item_height(*data.graph); //If use_bearing is false, it calculates a bearing depending on the current //position of the requested item. From 6e401fe75ec540f20427ddc72974bfd43c87571f Mon Sep 17 00:00:00 2001 From: Jinhao Date: Sun, 2 Dec 2018 05:13:20 +0800 Subject: [PATCH 091/149] add scheme for checkbox square box --- include/nana/gui/detail/widget_geometrics.hpp | 7 ++++- include/nana/gui/widgets/checkbox.hpp | 9 +++++- source/gui/detail/color_schemes.cpp | 29 ++++++++++++++++++- source/gui/widgets/checkbox.cpp | 16 ++++++---- 4 files changed, 52 insertions(+), 9 deletions(-) diff --git a/include/nana/gui/detail/widget_geometrics.hpp b/include/nana/gui/detail/widget_geometrics.hpp index 1cb8cd3e..d172536e 100644 --- a/include/nana/gui/detail/widget_geometrics.hpp +++ b/include/nana/gui/detail/widget_geometrics.hpp @@ -1,7 +1,7 @@ /* * Widget Geometrics * 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 @@ -22,12 +22,17 @@ namespace nana public: color_proxy(const color_proxy&); color_proxy(color_rgb); + color_proxy(color_argb); + color_proxy(color_rgba); color_proxy(colors); color_proxy& operator=(const color_proxy&); color_proxy& operator=(const ::nana::color&); color_proxy& operator=(color_rgb); + color_proxy& operator=(color_argb); + color_proxy& operator=(color_rgba); color_proxy& operator=(colors); color get_color() const; + color get(const color& default_color) const; operator color() const; private: std::shared_ptr color_; diff --git a/include/nana/gui/widgets/checkbox.hpp b/include/nana/gui/widgets/checkbox.hpp index c6f3a064..57239d20 100644 --- a/include/nana/gui/widgets/checkbox.hpp +++ b/include/nana/gui/widgets/checkbox.hpp @@ -37,6 +37,13 @@ namespace drawerbase { namespace checkbox { + struct scheme + : public widget_geometrics + { + color_proxy square_bgcolor{ static_cast(0x0) }; + color_proxy square_border_color{ colors::black }; + }; + struct events_type : public general_events { @@ -67,7 +74,7 @@ namespace drawerbase class checkbox - : public widget_object + : public widget_object { public: checkbox(); diff --git a/source/gui/detail/color_schemes.cpp b/source/gui/detail/color_schemes.cpp index d9fca610..c0555b6f 100644 --- a/source/gui/detail/color_schemes.cpp +++ b/source/gui/detail/color_schemes.cpp @@ -1,7 +1,7 @@ /* * Color Schemes * 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 @@ -24,6 +24,14 @@ namespace nana : color_(std::make_shared(clr)) {} + color_proxy::color_proxy(color_argb clr) + : color_(std::make_shared(clr)) + {} + + color_proxy::color_proxy(color_rgba clr) + : color_(std::make_shared(clr)) + {} + color_proxy::color_proxy(colors clr) : color_(std::make_shared(clr)) {} @@ -47,6 +55,18 @@ namespace nana return *this; } + color_proxy& color_proxy::operator = (color_argb clr) + { + color_ = std::make_shared<::nana::color>(clr); + return *this; + } + + color_proxy& color_proxy::operator = (color_rgba clr) + { + color_ = std::make_shared<::nana::color>(clr); + return *this; + } + color_proxy& color_proxy::operator = (colors clr) { color_ = std::make_shared<::nana::color>(clr); @@ -58,6 +78,13 @@ namespace nana return *color_; } + color color_proxy::get(const color& default_color) const + { + if (color_->invisible()) + return default_color; + return *color_; + } + color_proxy::operator color() const { return *color_; diff --git a/source/gui/widgets/checkbox.cpp b/source/gui/widgets/checkbox.cpp index 06a5f0ee..c458a4ac 100644 --- a/source/gui/widgets/checkbox.cpp +++ b/source/gui/widgets/checkbox.cpp @@ -24,6 +24,7 @@ namespace nana{ namespace drawerbase struct drawer::implement { widget * widget_ptr; + scheme * scheme_ptr; bool react; bool radio; facade crook; @@ -47,6 +48,7 @@ namespace nana{ namespace drawerbase void drawer::attached(widget_reference widget, graph_reference) { impl_->widget_ptr = &widget; + impl_->scheme_ptr =static_cast(API::dev::get_scheme(widget)); API::dev::enable_space_click(widget, true); } @@ -78,12 +80,14 @@ namespace nana{ namespace drawerbase } //draw crook -#ifdef _nana_std_has_string_view - auto txt_px = graph.text_extent_size(std::wstring_view( L"jN", 2 )).height + 2; -#else - auto txt_px = graph.text_extent_size(L"jN", 2).height + 2; -#endif - impl_->crook.draw(graph, wdg->bgcolor(), wdg->fgcolor(), rectangle(0, txt_px > 16 ? (txt_px - 16) / 2 : 0, 16, 16), API::element_state(*wdg)); + + unsigned txt_px = 0, descent = 0, ileading = 0; + graph.text_metrics(txt_px, descent, ileading); + txt_px += (descent + 2); + + impl_->crook.draw(graph, + impl_->scheme_ptr->square_bgcolor.get(wdg->bgcolor()), impl_->scheme_ptr->square_border_color.get(wdg->fgcolor()), + rectangle(0, txt_px > 16 ? (txt_px - 16) / 2 : 0, 16, 16), API::element_state(*wdg)); } void drawer::mouse_down(graph_reference graph, const arg_mouse&) From 54cfd3075de77d3ee243f6b3463ea07744effe5a Mon Sep 17 00:00:00 2001 From: Jinhao Date: Sun, 2 Dec 2018 06:44:11 +0800 Subject: [PATCH 092/149] add methods for listbox visible range --- include/nana/gui/widgets/listbox.hpp | 21 ++++++++++++ source/gui/widgets/listbox.cpp | 48 ++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/include/nana/gui/widgets/listbox.hpp b/include/nana/gui/widgets/listbox.hpp index ecdb9be8..d5bb0607 100644 --- a/include/nana/gui/widgets/listbox.hpp +++ b/include/nana/gui/widgets/listbox.hpp @@ -1555,6 +1555,27 @@ the nana::detail::basic_window member pointer scheme * @return the reference of *this. */ listbox& category_icon(const paint::image& img_expanded, const paint::image&& img_collapsed); + + /// Returns first visible element + /** + * It may return an item or a category item. + * @return the index of first visible element. + */ + index_pair first_visible() const; + + /// Returns last visible element + /** + * It may return an item or a category item. + * @return the index of last visible element. + */ + index_pair last_visible() const; + + /// Returns all visible items + /** + * It returns all visible items that are displayed in listbox window. + * @return index_pairs containing all visible items. + */ + index_pairs visibles() const; private: drawerbase::listbox::essence & _m_ess() const; nana::any* _m_anyobj(size_type cat, size_type index, bool allocate_if_empty) const override; diff --git a/source/gui/widgets/listbox.cpp b/source/gui/widgets/listbox.cpp index deb6d881..09c62a06 100644 --- a/source/gui/widgets/listbox.cpp +++ b/source/gui/widgets/listbox.cpp @@ -5989,6 +5989,54 @@ namespace nana return *this; } + auto listbox::first_visible() const ->index_pair + { + return _m_ess().first_display(); + } + + auto listbox::last_visible() const -> index_pair + { + return _m_ess().lister.advance(_m_ess().first_display(), _m_ess().count_of_exposed(true)); + } + + auto listbox::visibles() const -> index_pairs + { + index_pairs indexes; + + auto idx = _m_ess().first_display(); + + auto n = _m_ess().count_of_exposed(true); + while (n > 0) + { + if (idx.empty()) + break; + + if (idx.is_category()) + { + indexes.push_back(idx); + --n; + } + else + { + auto const count = (std::min)(_m_ess().lister.size_item(idx.cat) - idx.item, n); + for (std::size_t i = 0; i < count; ++i) + { + indexes.push_back(idx); + ++idx.item; + } + if (count) + { + n -= count; + --idx.item; + } + } + + idx = _m_ess().lister.advance(idx, 1); + } + + return indexes; + } + drawerbase::listbox::essence & listbox::_m_ess() const { return get_drawer_trigger().ess(); From f4c71db98b628b9030747bbf0580dfdf35896ecc Mon Sep 17 00:00:00 2001 From: Jinhao Date: Wed, 5 Dec 2018 06:36:40 +0800 Subject: [PATCH 093/149] refactor scroll --- include/nana/gui/widgets/scroll.hpp | 125 ++++++++++++-------------- source/gui/widgets/scroll.cpp | 133 +++++++++++++--------------- 2 files changed, 123 insertions(+), 135 deletions(-) diff --git a/include/nana/gui/widgets/scroll.hpp b/include/nana/gui/widgets/scroll.hpp index 81bc0a60..0407095a 100644 --- a/include/nana/gui/widgets/scroll.hpp +++ b/include/nana/gui/widgets/scroll.hpp @@ -67,30 +67,30 @@ namespace nana class drawer { public: - struct states + enum class states { - enum{ none, highlight, actived, selected }; + none, highlight, actived, selected }; using graph_reference = paint::graphics&; const static unsigned fixedsize = 16; // make it part of a new "metric" in the widget_scheme - drawer(metrics_type& m); - void set_vertical(bool); + drawer(bool vert); buttons what(graph_reference, const point&); void scroll_delta_pos(graph_reference, int); void auto_scroll(); - void draw(graph_reference, buttons); + void draw(graph_reference); + private: bool _m_check() const; void _m_adjust_scroll(graph_reference); void _m_background(graph_reference); - void _m_button_frame(graph_reference, ::nana::rectangle, int state); - void _m_draw_scroll(graph_reference, int state); - void _m_draw_button(graph_reference, ::nana::rectangle, buttons what, int state); - private: - metrics_type &metrics_; - bool vertical_; + void _m_button_frame(graph_reference, ::nana::rectangle, states state); + void _m_draw_scroll(graph_reference, states state); + void _m_draw_button(graph_reference, ::nana::rectangle, buttons what, states state); + public: + metrics_type metrics; + bool const vert; }; template @@ -101,35 +101,34 @@ namespace nana typedef metrics_type::size_type size_type; trigger() - : graph_(nullptr), drawer_(metrics_) + : graph_(nullptr), drawer_(Vertical) { - drawer_.set_vertical(Vertical); } const metrics_type& metrics() const { - return metrics_; + return drawer_.metrics; } void peak(size_type s) { - if (graph_ && (metrics_.peak != s)) + if (graph_ && (drawer_.metrics.peak != s)) { - metrics_.peak = s; + drawer_.metrics.peak = s; API::refresh_window(widget_->handle()); } } void value(size_type s) { - if (metrics_.range > metrics_.peak) + if (drawer_.metrics.range > drawer_.metrics.peak) s = 0; - else if (s + metrics_.range > metrics_.peak) - s = metrics_.peak - metrics_.range; + else if (s + drawer_.metrics.range > drawer_.metrics.peak) + s = drawer_.metrics.peak - drawer_.metrics.range; - if (graph_ && (metrics_.value != s)) + if (graph_ && (drawer_.metrics.value != s)) { - metrics_.value = s; + drawer_.metrics.value = s; _m_emit_value_changed(); API::refresh_window(*widget_); @@ -138,16 +137,16 @@ namespace nana void range(size_type s) { - if (graph_ && (metrics_.range != s)) + if (graph_ && (drawer_.metrics.range != s)) { - metrics_.range = s; + drawer_.metrics.range = s; API::refresh_window(widget_->handle()); } } void step(size_type s) { - metrics_.step = (s ? s : 1); + drawer_.metrics.step = (s ? s : 1); } bool make_step(bool forward, unsigned multiple) @@ -155,12 +154,12 @@ namespace nana if (!graph_) return false; - size_type step = (multiple > 1 ? metrics_.step * multiple : metrics_.step); - size_type value = metrics_.value; + size_type step = (multiple > 1 ? drawer_.metrics.step * multiple : drawer_.metrics.step); + size_type value = drawer_.metrics.value; if (forward) { - size_type maxv = metrics_.peak - metrics_.range; - if (metrics_.peak > metrics_.range && value < maxv) + size_type maxv = drawer_.metrics.peak - drawer_.metrics.range; + if (drawer_.metrics.peak > drawer_.metrics.range && value < maxv) { if (maxv - value >= step) value += step; @@ -175,8 +174,8 @@ namespace nana else value = 0; } - size_type cmpvalue = metrics_.value; - metrics_.value = value; + size_type cmpvalue = drawer_.metrics.value; + drawer_.metrics.value = value; if (value != cmpvalue) { _m_emit_value_changed(); @@ -202,47 +201,42 @@ namespace nana void refresh(graph_reference graph) override { - drawer_.draw(graph, metrics_.what); + drawer_.draw(graph); } void resized(graph_reference graph, const ::nana::arg_resized&) override { - drawer_.draw(graph, metrics_.what); + drawer_.draw(graph); API::dev::lazy_refresh(); } void mouse_enter(graph_reference graph, const ::nana::arg_mouse& arg) override { - metrics_.what = drawer_.what(graph, arg.pos); - drawer_.draw(graph, metrics_.what); + drawer_.metrics.what = drawer_.what(graph, arg.pos); + drawer_.draw(graph); API::dev::lazy_refresh(); } void mouse_move(graph_reference graph, const ::nana::arg_mouse& arg) override { - bool redraw = false; - if (metrics_.pressed && (metrics_.what == buttons::scroll)) + if (drawer_.metrics.pressed && (drawer_.metrics.what == buttons::scroll)) { - size_type cmpvalue = metrics_.value; + size_type cmpvalue = drawer_.metrics.value; drawer_.scroll_delta_pos(graph, (Vertical ? arg.pos.y : arg.pos.x)); - if (cmpvalue != metrics_.value) + if (cmpvalue != drawer_.metrics.value) _m_emit_value_changed(); - redraw = true; } else { buttons what = drawer_.what(graph, arg.pos); - if (metrics_.what != what) - { - redraw = true; - metrics_.what = what; - } - } - if (redraw) - { - drawer_.draw(graph, metrics_.what); - API::dev::lazy_refresh(); + if (drawer_.metrics.what == what) + return; //no change, don't redraw + + drawer_.metrics.what = what; } + + drawer_.draw(graph); + API::dev::lazy_refresh(); } void dbl_click(graph_reference graph, const arg_mouse& arg) override @@ -254,33 +248,33 @@ namespace nana { if (arg.left_button) { - metrics_.pressed = true; - metrics_.what = drawer_.what(graph, arg.pos); - switch (metrics_.what) + drawer_.metrics.pressed = true; + drawer_.metrics.what = drawer_.what(graph, arg.pos); + switch (drawer_.metrics.what) { case buttons::first: case buttons::second: - make_step(metrics_.what == buttons::second, 1); + make_step(drawer_.metrics.what == buttons::second, 1); timer_.interval(1000); timer_.start(); break; case buttons::scroll: widget_->set_capture(true); - metrics_.scroll_mouse_offset = (Vertical ? arg.pos.y : arg.pos.x) - metrics_.scroll_pos; + drawer_.metrics.scroll_mouse_offset = (Vertical ? arg.pos.y : arg.pos.x) - drawer_.metrics.scroll_pos; break; case buttons::forward: case buttons::backward: { - size_type cmpvalue = metrics_.value; + size_type cmpvalue = drawer_.metrics.value; drawer_.auto_scroll(); - if (cmpvalue != metrics_.value) + if (cmpvalue != drawer_.metrics.value) _m_emit_value_changed(); } break; default: //Ignore buttons::none break; } - drawer_.draw(graph, metrics_.what); + drawer_.draw(graph); API::dev::lazy_refresh(); } } @@ -291,18 +285,18 @@ namespace nana widget_->release_capture(); - metrics_.pressed = false; - metrics_.what = drawer_.what(graph, arg.pos); - drawer_.draw(graph, metrics_.what); + drawer_.metrics.pressed = false; + drawer_.metrics.what = drawer_.what(graph, arg.pos); + drawer_.draw(graph); API::dev::lazy_refresh(); } void mouse_leave(graph_reference graph, const arg_mouse&) override { - if (metrics_.pressed) return; + if (drawer_.metrics.pressed) return; - metrics_.what = buttons::none; - drawer_.draw(graph, buttons::none); + drawer_.metrics.what = buttons::none; + drawer_.draw(graph); API::dev::lazy_refresh(); } @@ -310,7 +304,7 @@ namespace nana { if (make_step(arg.upwards == false, 3)) { - drawer_.draw(graph, metrics_.what); + drawer_.draw(graph); API::dev::lazy_refresh(); } } @@ -322,14 +316,13 @@ namespace nana void _m_tick() { - make_step(metrics_.what == buttons::second, 1); + make_step(drawer_.metrics.what == buttons::second, 1); API::refresh_window(widget_->handle()); timer_.interval(100); } private: ::nana::scroll * widget_; nana::paint::graphics * graph_; - metrics_type metrics_; drawer drawer_; timer timer_; }; diff --git a/source/gui/widgets/scroll.cpp b/source/gui/widgets/scroll.cpp index 10b2c6da..d8e7d64a 100644 --- a/source/gui/widgets/scroll.cpp +++ b/source/gui/widgets/scroll.cpp @@ -33,21 +33,16 @@ namespace nana //end struct metrics_type //class drawer - drawer::drawer(metrics_type& m) - :metrics_(m) + drawer::drawer(bool vert) : + vert(vert) {} - void drawer::set_vertical(bool v) - { - vertical_ = v; - } - buttons drawer::what(graph_reference graph, const point& screen_pos) { unsigned scale; int pos; - if(vertical_) + if(vert) { scale = graph.height(); pos = screen_pos.y; @@ -64,15 +59,15 @@ namespace nana if (pos > static_cast(scale) - bound_pos) return buttons::second; - if(metrics_.scroll_length) + if(metrics.scroll_length) { - if(metrics_.scroll_pos + static_cast(fixedsize) <= pos && pos < metrics_.scroll_pos + static_cast(fixedsize + metrics_.scroll_length)) + if(metrics.scroll_pos + static_cast(fixedsize) <= pos && pos < metrics.scroll_pos + static_cast(fixedsize + metrics.scroll_length)) return buttons::scroll; } - if(static_cast(fixedsize) <= pos && pos < metrics_.scroll_pos) + if(static_cast(fixedsize) <= pos && pos < metrics.scroll_pos) return buttons::forward; - else if(metrics_.scroll_pos + static_cast(metrics_.scroll_length) <= pos && pos < static_cast(scale - fixedsize)) + else if(metrics.scroll_pos + static_cast(metrics.scroll_length) <= pos && pos < static_cast(scale - fixedsize)) return buttons::backward; return buttons::none; @@ -80,39 +75,39 @@ namespace nana void drawer::scroll_delta_pos(graph_reference graph, int mouse_pos) { - if(mouse_pos + metrics_.scroll_mouse_offset == metrics_.scroll_pos) return; + if(mouse_pos + metrics.scroll_mouse_offset == metrics.scroll_pos) return; - unsigned scale = vertical_ ? graph.height() : graph.width(); + unsigned scale = vert ? graph.height() : graph.width(); if(scale > fixedsize * 2) { - int pos = mouse_pos - metrics_.scroll_mouse_offset; - const unsigned scroll_area = static_cast(scale - fixedsize * 2 - metrics_.scroll_length); + int pos = mouse_pos - metrics.scroll_mouse_offset; + const unsigned scroll_area = static_cast(scale - fixedsize * 2 - metrics.scroll_length); if(pos < 0) pos = 0; else if(pos > static_cast(scroll_area)) pos = static_cast(scroll_area); - metrics_.scroll_pos = pos; - auto value_max = metrics_.peak - metrics_.range; + metrics.scroll_pos = pos; + auto value_max = metrics.peak - metrics.range; //Check scroll_area to avoiding division by zero. if (scroll_area) - metrics_.value = static_cast(pos * (static_cast(value_max) / scroll_area)); //converting to double to avoid overflow. + metrics.value = static_cast(pos * (static_cast(value_max) / scroll_area)); //converting to double to avoid overflow. - if (metrics_.value < value_max) + if (metrics.value < value_max) { //converting to double to avoid overflow. auto const px_per_value = static_cast(scroll_area) / value_max; - int selfpos = static_cast(metrics_.value * px_per_value); - int nextpos = static_cast((metrics_.value + 1) * px_per_value); + int selfpos = static_cast(metrics.value * px_per_value); + int nextpos = static_cast((metrics.value + 1) * px_per_value); if(selfpos != nextpos && (pos - selfpos > nextpos - pos)) - ++metrics_.value; + ++metrics.value; } else - metrics_.value = value_max; + metrics.value = value_max; } } @@ -121,46 +116,46 @@ namespace nana if (!_m_check()) return; - if(buttons::forward == metrics_.what) + if(buttons::forward == metrics.what) { //backward - if(metrics_.value <= metrics_.range) - metrics_.value = 0; + if(metrics.value <= metrics.range) + metrics.value = 0; else - metrics_.value -= (metrics_.range-1); + metrics.value -= (metrics.range-1); } - else if(buttons::backward == metrics_.what) + else if(buttons::backward == metrics.what) { - if(metrics_.peak - metrics_.range - metrics_.value <= metrics_.range) - metrics_.value = metrics_.peak - metrics_.range; + if(metrics.peak - metrics.range - metrics.value <= metrics.range) + metrics.value = metrics.peak - metrics.range; else - metrics_.value += (metrics_.range-1); + metrics.value += (metrics.range-1); } } - void drawer::draw(graph_reference graph, buttons what) + void drawer::draw(graph_reference graph) { - if(false == metrics_.pressed || metrics_.what != buttons::scroll) + if(false == metrics.pressed || metrics.what != buttons::scroll) _m_adjust_scroll(graph); _m_background(graph); - rectangle_rotator r(vertical_, ::nana::rectangle{ graph.size() }); + rectangle_rotator r(vert, ::nana::rectangle{ graph.size() }); r.x_ref() = static_cast(r.w() - fixedsize); r.w_ref() = fixedsize; - int state = ((_m_check() == false || what == buttons::none) ? states::none : states::highlight); - int moused_state = (_m_check() ? (metrics_.pressed ? states::selected : states::actived) : states::none); + auto state = ((_m_check() == false || metrics.what == buttons::none) ? states::none : states::highlight); + auto moused_state = (_m_check() ? (metrics.pressed ? states::selected : states::actived) : states::none); auto result = r.result(); //draw first - _m_draw_button(graph, { 0, 0, result.width, result.height }, buttons::first, (buttons::first == what ? moused_state : state)); + _m_draw_button(graph, { 0, 0, result.width, result.height }, buttons::first, (buttons::first == metrics.what ? moused_state : state)); //draw second - _m_draw_button(graph, result, buttons::second, (buttons::second == what ? moused_state : state)); + _m_draw_button(graph, result, buttons::second, (buttons::second == metrics.what ? moused_state : state)); //draw scroll - _m_draw_scroll(graph, (buttons::scroll == what ? moused_state : states::highlight)); + _m_draw_scroll(graph, (buttons::scroll == metrics.what ? moused_state : states::highlight)); } //private: @@ -168,19 +163,19 @@ namespace nana { graph.rectangle(true, {0xf0, 0xf0, 0xf0}); - if (!metrics_.pressed || !_m_check()) + if (!metrics.pressed || !_m_check()) return; - nana::rectangle_rotator r(vertical_, ::nana::rectangle{ graph.size() }); - if(metrics_.what == buttons::forward) + nana::rectangle_rotator r(vert, ::nana::rectangle{ graph.size() }); + if(metrics.what == buttons::forward) { r.x_ref() = static_cast(fixedsize); - r.w_ref() = metrics_.scroll_pos; + r.w_ref() = metrics.scroll_pos; } - else if(buttons::backward == metrics_.what) + else if(buttons::backward == metrics.what) { - r.x_ref() = static_cast(fixedsize + metrics_.scroll_pos + metrics_.scroll_length); - r.w_ref() = static_cast((vertical_ ? graph.height() : graph.width()) - (fixedsize * 2 + metrics_.scroll_pos + metrics_.scroll_length)); + r.x_ref() = static_cast(fixedsize + metrics.scroll_pos + metrics.scroll_length); + r.w_ref() = static_cast((vert ? graph.height() : graph.width()) - (fixedsize * 2 + metrics.scroll_pos + metrics.scroll_length)); } else return; @@ -190,9 +185,9 @@ namespace nana graph.rectangle(result, true, static_cast(0xDCDCDC)); } - void drawer::_m_button_frame(graph_reference graph, rectangle r, int state) + void drawer::_m_button_frame(graph_reference graph, rectangle r, states state) { - if (!state) + if (states::none == state) return; ::nana::color clr{0x97, 0x97, 0x97}; //highlight @@ -210,7 +205,7 @@ namespace nana graph.palette(false, clr); r.pare_off(2); - if(vertical_) + if(vert) { unsigned half = r.width / 2; graph.rectangle({ r.x + static_cast(r.width - half), r.y, half, r.height }, true); @@ -222,19 +217,19 @@ namespace nana graph.rectangle({r.x, r.y + static_cast(r.height - half), r.width, half}, true); r.height -= half; } - graph.gradual_rectangle(r, colors::white, clr, !vertical_); + graph.gradual_rectangle(r, colors::white, clr, !vert); } bool drawer::_m_check() const { - return (metrics_.scroll_length && metrics_.range && (metrics_.peak > metrics_.range)); + return (metrics.scroll_length && metrics.range && (metrics.peak > metrics.range)); } void drawer::_m_adjust_scroll(graph_reference graph) { - if(metrics_.range == 0 || metrics_.peak <= metrics_.range) return; + if(metrics.range == 0 || metrics.peak <= metrics.range) return; - unsigned pixels = vertical_ ? graph.height() : graph.width(); + unsigned pixels = vert ? graph.height() : graph.width(); int pos = 0; unsigned len = 0; @@ -242,38 +237,38 @@ namespace nana if(pixels > fixedsize * 2) { pixels -= (fixedsize * 2); - len = static_cast(pixels * metrics_.range / metrics_.peak); + len = static_cast(pixels * metrics.range / metrics.peak); if(len < fixedsize) len = fixedsize; - if(metrics_.value) + if(metrics.value) { pos = static_cast(pixels - len); - if(metrics_.value + metrics_.range >= metrics_.peak) - metrics_.value = metrics_.peak - metrics_.range; + if(metrics.value + metrics.range >= metrics.peak) + metrics.value = metrics.peak - metrics.range; else - pos = static_cast((metrics_.value * pos) /(metrics_.peak - metrics_.range)); + pos = static_cast((metrics.value * pos) /(metrics.peak - metrics.range)); } } - metrics_.scroll_pos = pos; - metrics_.scroll_length = len; + metrics.scroll_pos = pos; + metrics.scroll_length = len; } - void drawer::_m_draw_scroll(graph_reference graph, int state) + void drawer::_m_draw_scroll(graph_reference graph, states state) { if(_m_check()) { - rectangle_rotator r(vertical_, rectangle{ graph.size() }); - r.x_ref() = static_cast(fixedsize + metrics_.scroll_pos); - r.w_ref() = static_cast(metrics_.scroll_length); + rectangle_rotator r(vert, rectangle{ graph.size() }); + r.x_ref() = static_cast(fixedsize + metrics.scroll_pos); + r.w_ref() = static_cast(metrics.scroll_length); _m_button_frame(graph, r.result(), state); } } - void drawer::_m_draw_button(graph_reference graph, rectangle r, buttons what, int state) + void drawer::_m_draw_button(graph_reference graph, rectangle r, buttons what, states state) { if(_m_check()) _m_button_frame(graph, r, state); @@ -287,7 +282,7 @@ namespace nana direction dir; if (buttons::second == what) { - if (vertical_) + if (vert) { r.y = top; dir = direction::south; @@ -299,9 +294,9 @@ namespace nana } } else - dir = vertical_ ? direction::north : direction::west; + dir = vert ? direction::north : direction::west; - if (vertical_) + if (vert) r.x = left / 2; else r.y = top / 2; From 48d3f4f0441684b464cc6d1da132e74994faf4d5 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Thu, 6 Dec 2018 07:41:02 +0800 Subject: [PATCH 094/149] fix bug that scrollbar steals input focus(#344) --- include/nana/gui/widgets/scroll.hpp | 3 +++ source/gui/widgets/skeletons/content_view.cpp | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/include/nana/gui/widgets/scroll.hpp b/include/nana/gui/widgets/scroll.hpp index 0407095a..62857c6f 100644 --- a/include/nana/gui/widgets/scroll.hpp +++ b/include/nana/gui/widgets/scroll.hpp @@ -190,6 +190,9 @@ namespace nana widget_ = static_cast< ::nana::scroll*>(&widget); widget.caption("nana scroll"); + //scroll doesn't want the keyboard focus. + API::take_active(widget, false, widget.parent()); + timer_.stop(); timer_.elapse(std::bind(&trigger::_m_tick, this)); } diff --git a/source/gui/widgets/skeletons/content_view.cpp b/source/gui/widgets/skeletons/content_view.cpp index 0596b4c6..9c004f17 100644 --- a/source/gui/widgets/skeletons/content_view.cpp +++ b/source/gui/widgets/skeletons/content_view.cpp @@ -224,7 +224,6 @@ namespace nana { { 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; } @@ -255,7 +254,6 @@ namespace nana { { 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; } From fc3478b22048b1f548f410b9b6ef78c6bff33986 Mon Sep 17 00:00:00 2001 From: besh81 Date: Thu, 6 Dec 2018 09:03:51 +0100 Subject: [PATCH 095/149] updated button.icon Now passing an empty image to button.icon() the previous image is erased from button --- source/gui/widgets/button.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/source/gui/widgets/button.cpp b/source/gui/widgets/button.cpp index d407f0eb..eae1923e 100644 --- a/source/gui/widgets/button.cpp +++ b/source/gui/widgets/button.cpp @@ -379,7 +379,15 @@ namespace nana{ namespace drawerbase void trigger::icon(const nana::paint::image& img) { - if(img.empty()) return; + if(img.empty()) + { + if(attr_.icon) + { + delete attr_.icon; + attr_.icon = 0; + } + return; + } if(nullptr == attr_.icon) attr_.icon = new paint::image; From 6378c3bf3c0c6cd782e14d47312feeb4222ba40c Mon Sep 17 00:00:00 2001 From: Jinhao Date: Fri, 7 Dec 2018 05:30:11 +0800 Subject: [PATCH 096/149] add contribution information --- include/nana/gui/widgets/button.hpp | 11 +++++++++-- source/gui/widgets/button.cpp | 7 ++----- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/include/nana/gui/widgets/button.hpp b/include/nana/gui/widgets/button.hpp index 9be71fd6..5dc4b9b4 100644 --- a/include/nana/gui/widgets/button.hpp +++ b/include/nana/gui/widgets/button.hpp @@ -1,13 +1,15 @@ /** * A Button 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 * http://www.boost.org/LICENSE_1_0.txt) * * @file: nana/gui/widgets/button.hpp + * @contributor + * besh81(pr#361) */ #ifndef NANA_GUI_WIDGET_BUTTON_HPP @@ -90,7 +92,12 @@ namespace nana{ button(window, const char* caption, bool visible = true); button(window, const nana::rectangle& = rectangle(), bool visible = true); - button& icon(const nana::paint::image&); + /// Shows an icon in front of caption + /** + * @param image Icon to be shown. If image is empty, the current icon is erased from the button. + * @return the reference of *this. + */ + button& icon(const nana::paint::image& image); button& enable_pushed(bool); bool pushed() const; button& pushed(bool); diff --git a/source/gui/widgets/button.cpp b/source/gui/widgets/button.cpp index eae1923e..96ff7c25 100644 --- a/source/gui/widgets/button.cpp +++ b/source/gui/widgets/button.cpp @@ -381,11 +381,8 @@ namespace nana{ namespace drawerbase { if(img.empty()) { - if(attr_.icon) - { - delete attr_.icon; - attr_.icon = 0; - } + delete attr_.icon; + attr_.icon = nullptr; return; } From e7bc1405eced585120b9facb6b024d0daf4a80ba Mon Sep 17 00:00:00 2001 From: James Bremner Date: Sun, 9 Dec 2018 16:01:32 -0500 Subject: [PATCH 097/149] Prevent slider adorn moving when slider disabled Fix suggested be Error Flynn in http://nanapro.org/en-us/forum/index.php?u=/topic/1059/ggslider-appearance-when-disabled#post-2498 --- source/gui/widgets/slider.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/gui/widgets/slider.cpp b/source/gui/widgets/slider.cpp index 20109237..67a67631 100644 --- a/source/gui/widgets/slider.cpp +++ b/source/gui/widgets/slider.cpp @@ -758,6 +758,10 @@ namespace nana void trigger::mouse_move(graph_reference graph, const arg_mouse& arg) { + // check if slider is disabled + if(!API::get_widget(arg.window_handle)->enabled()) + return; // do nothing + bool updated = false; if (model_ptr_->if_trace_slider()) { From c09e7b7ba62372268542242d650a7d6da3781dea Mon Sep 17 00:00:00 2001 From: Jinhao Date: Mon, 10 Dec 2018 07:22:16 +0800 Subject: [PATCH 098/149] improve nana.filesystem --- include/nana/filesystem/filesystem.hpp | 37 +-- include/nana/filesystem/filesystem_ext.hpp | 20 +- include/nana/gui/filebox.hpp | 2 +- source/filesystem/filesystem.cpp | 280 +++++++++++++++++++-- source/gui/filebox.cpp | 6 +- source/gui/widgets/listbox.cpp | 2 +- 6 files changed, 295 insertions(+), 52 deletions(-) diff --git a/include/nana/filesystem/filesystem.hpp b/include/nana/filesystem/filesystem.hpp index d5c0a7d9..520cdf1c 100644 --- a/include/nana/filesystem/filesystem.hpp +++ b/include/nana/filesystem/filesystem.hpp @@ -234,7 +234,7 @@ namespace nana { namespace experimental { namespace filesystem } // modifiers - //void clear() noexcept; + void clear() noexcept; path& make_preferred(); path& remove_filename(); //path& replace_filename(const path& replacement); @@ -242,10 +242,10 @@ namespace nana { namespace experimental { namespace filesystem //void swap(path& rhs) noexcept; // decomposition - //path root_name() const; - //path root_directory() const; - //path root_path() const; - //path relative_path() const; + path root_name() const; + path root_directory() const; + path root_path() const; + path relative_path() const; path parent_path() const; path filename() const; //path stem() const; @@ -253,16 +253,16 @@ namespace nana { namespace experimental { namespace filesystem // query bool empty() const noexcept; - //bool has_root_name() const; - //bool has_root_directory() const; - //bool has_root_path() const; - //bool has_relative_path() const; - bool has_parent_path() const { return !parent_path().string().empty(); }; // temp;; - bool has_filename() const { return !filename().string().empty(); }; // temp; + bool has_root_name() const { return !root_name().empty(); } + bool has_root_directory() const { return !root_directory().empty(); } + bool has_root_path() const { return !root_path().empty(); } + bool has_relative_path() const { return !relative_path().empty(); } + bool has_parent_path() const { return !parent_path().empty(); }; // temp;; + bool has_filename() const { return !filename().empty(); }; // temp; //bool has_stem() const; - bool has_extension() const { return !extension().string().empty(); }; // temp - //bool is_absolute() const; - //bool is_relative() const; + bool has_extension() const { return !extension().empty(); }; // temp + bool is_absolute() const; + bool is_relative() const; int compare(const path& other) const; @@ -523,11 +523,20 @@ namespace std { # else using namespace nana::experimental::filesystem::v1; # endif + } // filesystem } // experimental namespace filesystem { using namespace std::experimental::filesystem; + +#if (defined(_MSC_VER) && ((!defined(_MSVC_LANG)) || (_MSVC_LANG < 201703))) + path absolute(const path& p); + path absolute(const path& p, std::error_code& err); + + path canonical(const path& p); + path canonical(const path& p, std::error_code& err); +#endif } } // std diff --git a/include/nana/filesystem/filesystem_ext.hpp b/include/nana/filesystem/filesystem_ext.hpp index ca579e8e..7bc69f0c 100644 --- a/include/nana/filesystem/filesystem_ext.hpp +++ b/include/nana/filesystem/filesystem_ext.hpp @@ -33,10 +33,10 @@ namespace filesystem_ext constexpr auto const def_rootname = "Root/"; #endif -std::experimental::filesystem::path path_user(); ///< extention ? +std::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) +inline std::string generic_u8string(const std::filesystem::path& p) { #if NANA_USING_BOOST_FILESYSTEM return nana::to_utf8(p.generic_wstring()); @@ -45,15 +45,15 @@ inline std::string generic_u8string(const std::experimental::filesystem::path& p #endif } -inline bool is_directory(const std::experimental::filesystem::directory_entry& dir) noexcept +inline bool is_directory(const std::filesystem::directory_entry& dir) noexcept { return is_directory(dir.status()); } //template // DI = directory_iterator from std, boost, or nana : return directory_entry -class directory_only_iterator : public std::experimental::filesystem::directory_iterator +class directory_only_iterator : public std::filesystem::directory_iterator { - using directory_iterator = std::experimental::filesystem::directory_iterator; + using directory_iterator = std::filesystem::directory_iterator; directory_only_iterator& find_first() { @@ -93,9 +93,9 @@ inline directory_only_iterator end(const directory_only_iterator&) noexcept } //template // DI = directory_iterator from std, boost, or nana : value_type directory_entry -class regular_file_only_iterator : public std::experimental::filesystem::directory_iterator +class regular_file_only_iterator : public std::filesystem::directory_iterator { - using directory_iterator = std::experimental::filesystem::directory_iterator; + using directory_iterator = std::filesystem::directory_iterator; regular_file_only_iterator& find_first() { while (((*this) != directory_iterator{}) && !is_regular_file((**this).status())) @@ -128,11 +128,11 @@ inline regular_file_only_iterator end(const regular_file_only_iterator&) noexcep return{}; } -std::string pretty_file_size(const std::experimental::filesystem::path& path); +std::string pretty_file_size(const std::filesystem::path& path); -std::string pretty_file_date(const std::experimental::filesystem::path& path); +std::string pretty_file_date(const std::filesystem::path& path); -bool modified_file_time(const std::experimental::filesystem::path& p, struct tm&); ///< extention ? +bool modified_file_time(const std::filesystem::path& p, struct tm&); ///< extention ? } // filesystem_ext } // nana diff --git a/include/nana/gui/filebox.hpp b/include/nana/gui/filebox.hpp index 812770ee..d157e336 100644 --- a/include/nana/gui/filebox.hpp +++ b/include/nana/gui/filebox.hpp @@ -92,7 +92,7 @@ namespace nana folderbox(folderbox&&) = delete; folderbox& operator=(folderbox&&) = delete; public: - using path_type = std::experimental::filesystem::path; + using path_type = std::filesystem::path; explicit folderbox(window owner = nullptr, const path_type& init_path = {}, std::string title={}); ~folderbox(); diff --git a/source/filesystem/filesystem.cpp b/source/filesystem/filesystem.cpp index ee636f18..bb83039c 100644 --- a/source/filesystem/filesystem.cpp +++ b/source/filesystem/filesystem.cpp @@ -45,7 +45,7 @@ #include #endif -namespace fs = std::experimental::filesystem; +namespace fs = std::filesystem; namespace nana { @@ -117,12 +117,7 @@ namespace nana if (ftime == ((fs::file_time_type::min)())) return{}; - //std::time_t cftime = decltype(ftime)::clock::to_time_t(ftime); - - //A workaround for VC2013 - using time_point = decltype(ftime); - - auto cftime = time_point::clock::to_time_t(ftime); + auto cftime = static_cast(ftime.time_since_epoch().count()); std::stringstream tm; tm << std::put_time(std::localtime(&cftime), "%Y-%m-%d, %H:%M:%S"); @@ -209,9 +204,9 @@ namespace nana { namespace experimental { namespace filesystem //Because of No wide character version of POSIX #if defined(NANA_POSIX) - const char* splstr = "/"; + const char* separators = "/"; #else - const wchar_t* splstr = L"/\\"; + const wchar_t* separators = L"/\\"; #endif //class file_status @@ -249,13 +244,22 @@ namespace nana { namespace experimental { namespace filesystem /// true if the path is empty, false otherwise. ?? bool path::empty() const noexcept { -#if defined(NANA_WINDOWS) - return (::GetFileAttributes(pathstr_.c_str()) == INVALID_FILE_ATTRIBUTES); -#elif defined(NANA_POSIX) - struct stat sta; - return (::stat(pathstr_.c_str(), &sta) == -1); + return pathstr_.empty(); + } + + bool path::is_absolute() const + { +#ifdef NANA_WINDOWS + return has_root_name() && has_root_directory(); +#else + return has_root_directory(); #endif } + + bool path::is_relative() const + { + return !is_absolute(); + } path path::extension() const { @@ -277,6 +281,107 @@ namespace nana { namespace experimental { namespace filesystem return path(pathstr_.substr(pos)); } + bool is_directory_separator(path::value_type ch) + { + return (ch == '/') +#ifdef NANA_WINDOWS + || (ch == path::preferred_separator) +#endif + ; + } + + + + path path::root_name() const + { + auto pos = pathstr_.find_first_not_of(separators); + if (pathstr_.npos != pos) + { + pos = pathstr_.find_first_of(separators, pos + 1); + if (pathstr_.npos != pos) + { + if ((is_directory_separator(pathstr_[0]) && is_directory_separator(pathstr_[1])) +#ifdef NANA_WINDOWS + || (pathstr_[pos - 1] == ':') +#endif + ) + return pathstr_.substr(0, pos); + } + } + + return path{}; + } + + std::size_t root_directory_pos(const path::string_type& str) + { +#ifdef NANA_WINDOWS + // case "c:/" + if (str.size() > 2 + && str[1] == ':' + && is_directory_separator(str[2])) return 2; +#endif + + // case "//" + if (str.size() == 2 + && is_directory_separator(str[0]) + && is_directory_separator(str[1])) return path::string_type::npos; + +#ifdef NANA_WINDOWS + // case "\\?\" + if (str.size() > 4 + && is_directory_separator(str[0]) + && is_directory_separator(str[1]) + && str[2] == '?' + && is_directory_separator(str[3])) + { + auto pos = str.find_first_of(separators, 4); + return pos < str.size() ? pos : str.npos; + } +#endif + + // case "//net {/}" + if (str.size() > 3 + && is_directory_separator(str[0]) + && is_directory_separator(str[1]) + && !is_directory_separator(str[2])) + { + auto pos = str.find_first_of(separators, 2); + return pos < str.size() ? pos : str.npos; + } + + // case "/" + if (str.size() > 0 && is_directory_separator(str[0])) return 0; + + return str.npos; + } + + path path::root_directory() const + { + auto pos = root_directory_pos(pathstr_); + + return pos == string_type::npos + ? path() + : path(pathstr_.substr(pos, 1)); + } + + path path::root_path() const + { + return root_name().pathstr_ + root_directory().pathstr_; + } + + path path::relative_path() const + { + if (!empty()) + { + auto pos = root_directory_pos(pathstr_); + + pos = pathstr_.find_first_not_of(separators, pos); + if (pathstr_.npos != pos) + return path{ pathstr_.substr(pos) }; + } + return{}; + } + path path::parent_path() const { return{nana_fs::parent_path(pathstr_)}; @@ -310,14 +415,14 @@ namespace nana { namespace experimental { namespace filesystem path path::filename() const { - auto pos = pathstr_.find_last_of(splstr); + auto pos = pathstr_.find_last_of(separators); if (pos != pathstr_.npos) { if (pos + 1 == pathstr_.size()) { value_type tmp[2] = {preferred_separator, 0}; - if (pathstr_.npos != pathstr_.find_last_not_of(splstr, pos)) + if (pathstr_.npos != pathstr_.find_last_not_of(separators, pos)) tmp[0] = '.'; return{ tmp }; @@ -328,6 +433,19 @@ namespace nana { namespace experimental { namespace filesystem return{ pathstr_ }; } + void path::clear() noexcept + { + pathstr_.clear(); + } + + path& path::make_preferred() + { +#ifdef NANA_WINDOWS + std::replace(pathstr_.begin(), pathstr_.end(), L'/', L'\\'); +#endif + return *this; + } + path& path::remove_filename() { #ifdef NANA_WINDOWS @@ -418,10 +536,10 @@ namespace nana { namespace experimental { namespace filesystem if (this == &p) { auto other = p.pathstr_; - if ((other.front() != '/') && (other.front() == path::preferred_separator)) + if (!is_directory_separator(other.front())) { - if (!this->pathstr_.empty() && (pathstr_.back() != '/' && pathstr_.back() == path::preferred_separator)) - pathstr_.append(path::preferred_separator, 1); + if (!pathstr_.empty() && !is_directory_separator(pathstr_.back())) + pathstr_.append(1, path::preferred_separator); } pathstr_ += other; @@ -429,10 +547,10 @@ namespace nana { namespace experimental { namespace filesystem else { auto & other = p.pathstr_; - if ((other.front() != '/') && (other.front() == path::preferred_separator)) + if (!is_directory_separator(other.front())) { - if (!this->pathstr_.empty() && (pathstr_.back() != '/' && pathstr_.back() == path::preferred_separator)) - pathstr_.append(path::preferred_separator, 1); + if (!pathstr_.empty() && !is_directory_separator(pathstr_.back())) + pathstr_.append(1, path::preferred_separator); } pathstr_ += p.pathstr_; @@ -976,5 +1094,123 @@ namespace nana { namespace experimental { namespace filesystem }//end namespace filesystem } //end namespace experimental }//end namespace nana + +#if (defined(_MSC_VER) && ((!defined(_MSVC_LANG)) || (_MSVC_LANG < 201703))) +namespace std +{ + namespace filesystem + { + path absolute(const path& p) + { + if (p.empty()) + return p; + + auto abs_base = current_path(); + + // store expensive to compute values that are needed multiple times + path p_root_name(p.root_name()); + path base_root_name(abs_base.root_name()); + path p_root_directory(p.root_directory()); + + if (!p_root_name.empty()) // p.has_root_name() + { + if (p_root_directory.empty()) // !p.has_root_directory() + return p_root_name / abs_base.root_directory() + / abs_base.relative_path() / p.relative_path(); + // p is absolute, so fall through to return p at end of block + } + else if (!p_root_directory.empty()) // p.has_root_directory() + { +#ifdef NANA_POSIX + // POSIX can have root name it it is a network path + if (base_root_name.empty()) // !abs_base.has_root_name() + return p; +#endif + return base_root_name / p; + } + else + return abs_base / p; + + return p; // p.is_absolute() is true + } + + path absolute(const path& p, std::error_code& err) + { + return absolute(p); + } + + path canonical(const path& p, std::error_code* err) + { + path source(p.is_absolute() ? p : absolute(p)); + path root(source.root_path()); + path result; + + std::error_code local_ec; + file_status stat(status(source, local_ec)); + + if (stat.type() == file_type::not_found) + { + if (nullptr == err) + throw (filesystem_error( + "nana::filesystem::canonical", source, + error_code(static_cast(errc::no_such_file_or_directory), generic_category()))); + err->assign(static_cast(errc::no_such_file_or_directory), generic_category()); + return result; + } + else if (local_ec) + { + if (nullptr == err) + throw (filesystem_error( + "nana::filesystem::canonical", source, local_ec)); + *err = local_ec; + return result; + } + + + auto tmp_p = source; + + std::vector source_elements; + while (tmp_p != root) + { + source_elements.emplace(source_elements.begin(), tmp_p.filename()); + tmp_p.remove_filename(); + } + + result = root; + + for(auto & e : source_elements) + { + auto str = e.string(); + if("." == str) + continue; + else if(".." == str) + { + if(result != root) + result.remove_filename(); + continue; + } + + result /= e; + } + + if (err) + err->clear(); + + return result; + } + + path canonical(const path& p) + { + return canonical(p, nullptr); + } + + path canonical(const path& p, std::error_code& err) + { + return canonical(p, &err); + } + }//end namespace filesystem +}//end namespace std +#endif + #endif diff --git a/source/gui/filebox.cpp b/source/gui/filebox.cpp index 36ff2ef9..3faaf2a1 100644 --- a/source/gui/filebox.cpp +++ b/source/gui/filebox.cpp @@ -37,7 +37,7 @@ # include #endif -namespace fs = std::experimental::filesystem; +namespace fs = std::filesystem; namespace fs_ext = nana::filesystem_ext; @@ -1295,15 +1295,13 @@ namespace nana std::wstring init_path = impl_->init_path.wstring(); // https://docs.microsoft.com/en-us/windows/desktop/api/shlobj_core/ns-shlobj_core-_browseinfoa - BROWSEINFO brw = { 0 }; + BROWSEINFO brw = { 0 }; brw.hwndOwner = reinterpret_cast(API::root(impl_->owner)); - //brw.pidlRoot; // specifies the location of the root folder from which to start browsing. brw.pszDisplayName = display_text; // buffer to receive the display name of the folder selected by the user. brw.lpszTitle = title.data(); brw.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE; // | BIF_EDITBOX; brw.lpfn = browse_folder_callback; brw.lParam = reinterpret_cast(init_path.c_str()); - //brw.iImage; // auto pidl = ::SHBrowseForFolder(&brw); if (pidl) diff --git a/source/gui/widgets/listbox.cpp b/source/gui/widgets/listbox.cpp index 09c62a06..eb302d9a 100644 --- a/source/gui/widgets/listbox.cpp +++ b/source/gui/widgets/listbox.cpp @@ -5996,7 +5996,7 @@ namespace nana auto listbox::last_visible() const -> index_pair { - return _m_ess().lister.advance(_m_ess().first_display(), _m_ess().count_of_exposed(true)); + return _m_ess().lister.advance(_m_ess().first_display(), static_cast(_m_ess().count_of_exposed(true))); } auto listbox::visibles() const -> index_pairs From 3f446e9a0c6cedfde4670db127bed847ef28c2be Mon Sep 17 00:00:00 2001 From: Jinhao Date: Wed, 12 Dec 2018 00:18:07 +0800 Subject: [PATCH 099/149] add directory_options --- include/nana/filesystem/filesystem.hpp | 12 +++++++++-- source/filesystem/filesystem.cpp | 30 +++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/include/nana/filesystem/filesystem.hpp b/include/nana/filesystem/filesystem.hpp index 520cdf1c..9d9c62f5 100644 --- a/include/nana/filesystem/filesystem.hpp +++ b/include/nana/filesystem/filesystem.hpp @@ -175,7 +175,13 @@ namespace nana { namespace experimental { namespace filesystem unknown = 0xFFFF ///< not known, such as when a file_status object is created without specifying the permissions }; //enum class copy_options; - //enum class directory_options; + + enum class directory_options + { + none, + follow_directory_symlink, + skip_permission_denied + }; struct space_info { @@ -357,7 +363,8 @@ namespace nana { namespace experimental { namespace filesystem public: directory_iterator() noexcept; - explicit directory_iterator(const path& dir); + explicit directory_iterator(const path& p); + directory_iterator(const path& p, directory_options opt); const value_type& operator*() const; const value_type* operator->() const; @@ -381,6 +388,7 @@ namespace nana { namespace experimental { namespace filesystem private: bool end_{false}; path::string_type path_; + directory_options option_{ directory_options::opt }; std::shared_ptr find_ptr_; find_handle handle_{nullptr}; diff --git a/source/filesystem/filesystem.cpp b/source/filesystem/filesystem.cpp index bb83039c..aa2c6ab4 100644 --- a/source/filesystem/filesystem.cpp +++ b/source/filesystem/filesystem.cpp @@ -102,6 +102,22 @@ namespace nana std::string pretty_file_date(const fs::path& path) // todo: move to .cpp { + struct tm t; + if (modified_file_time(path, t)) + { + std::stringstream tm; + tm << std::put_time(&t, "%Y-%m-%d, %H:%M:%S"); + return tm.str(); + } + return {}; + +/* + // Deprecated + //Windows stores file times using the FILETIME structure, which is a 64 bit value of 100ns intervals from January 1, 1601. + //What's worse is that this 1601 date is fairly common to see given that it's the all zeroes value, and it is far before the + //earliest date representable with time_t.std::filesystem can't change the reality of the underlying platform. + + try { #if NANA_USING_BOOST_FILESYSTEM // The return type of boost::filesystem::last_write_time isn't @@ -117,7 +133,9 @@ namespace nana if (ftime == ((fs::file_time_type::min)())) return{}; - auto cftime = static_cast(ftime.time_since_epoch().count()); + //A workaround for VC2013 + using time_point = decltype(ftime); + auto cftime = time_point::clock::to_time_t(ftime); std::stringstream tm; tm << std::put_time(std::localtime(&cftime), "%Y-%m-%d, %H:%M:%S"); @@ -126,6 +144,8 @@ namespace nana catch (...) { return{}; } +#endif +*/ } bool modified_file_time(const fs::path& p, struct tm& t) @@ -442,6 +462,8 @@ namespace nana { namespace experimental { namespace filesystem { #ifdef NANA_WINDOWS std::replace(pathstr_.begin(), pathstr_.end(), L'/', L'\\'); +#else + std::replace(pathstr_.begin(), pathstr_.end(), '\\', '/'); #endif return *this; } @@ -652,6 +674,12 @@ namespace nana { namespace experimental { namespace filesystem _m_prepare(file_path); } + directory_iterator::directory_iterator(const path& p, directory_options opt): + option_(opt) + { + _m_prepare(p); + } + const directory_iterator::value_type& directory_iterator::operator*() const { return value_; } const directory_iterator::value_type* From 5c50bd7a56291c4d818a00c7eff16c10b33ea317 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Wed, 12 Dec 2018 00:21:06 +0800 Subject: [PATCH 100/149] treebox treats root paths / and \ as a node --- include/nana/gui/widgets/detail/tree_cont.hpp | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/include/nana/gui/widgets/detail/tree_cont.hpp b/include/nana/gui/widgets/detail/tree_cont.hpp index 2679bc64..3aa3b4f9 100644 --- a/include/nana/gui/widgets/detail/tree_cont.hpp +++ b/include/nana/gui/widgets/detail/tree_cont.hpp @@ -1,6 +1,6 @@ /* * A Tree Container class 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 @@ -479,6 +479,8 @@ namespace detail return nullptr; } + // foreach elements of key. + // the root name "/" and "\\" are treated as a node. This is a feature that can easily support the UNIX-like path. template void _m_for_each(const ::std::string& key, Function function) const { @@ -491,18 +493,24 @@ namespace detail { if(beg != end) { - if(function(key.substr(beg, end - beg)) == false) + if(!function(key.substr(beg, end - beg))) return; } auto next = key.find_first_not_of("\\/", end); - if(next != ::std::string::npos) - { - beg = next; - end = key.find_first_of("\\/", beg); - } - else + + if ((next == ::std::string::npos) && end) return; + + if (0 == end) + { + if ((!function(key.substr(0, 1))) || (next == ::std::string::npos)) + return; + } + + beg = next; + end = key.find_first_of("\\/", beg); + } function(key.substr(beg, key.size() - beg)); From 43d85b300a8cc0754c60255d6425e2d9fb77f5a1 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Wed, 12 Dec 2018 00:23:06 +0800 Subject: [PATCH 101/149] fix a label's bug in wrap mode --- source/gui/widgets/label.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/gui/widgets/label.cpp b/source/gui/widgets/label.cpp index 74bedf81..f74ec52f 100644 --- a/source/gui/widgets/label.cpp +++ b/source/gui/widgets/label.cpp @@ -338,11 +338,11 @@ namespace nana #else rs.vslines.emplace_back(); auto & vsline = rs.vslines.back(); - +#endif vsline.baseline = 0; vsline.extent_height_px = def_line_px; vsline.x_base = 0; -#endif + return 0; } From a2cda83019d2458755e96a331f6a6e998f5c0433 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Wed, 12 Dec 2018 00:39:54 +0800 Subject: [PATCH 102/149] refactor shared_icons --- source/detail/posix/shared_icons.cpp | 50 ++++++++++++++ source/detail/posix/shared_icons.hpp | 28 ++++++++ source/detail/posix/xdnd_protocol.hpp | 98 +++++++++++++++++++++++++-- source/gui/dragdrop.cpp | 51 +------------- source/gui/filebox.cpp | 1 + source/gui/widgets/scroll.cpp | 1 + 6 files changed, 172 insertions(+), 57 deletions(-) create mode 100644 source/detail/posix/shared_icons.cpp create mode 100644 source/detail/posix/shared_icons.hpp diff --git a/source/detail/posix/shared_icons.cpp b/source/detail/posix/shared_icons.cpp new file mode 100644 index 00000000..c6345db7 --- /dev/null +++ b/source/detail/posix/shared_icons.cpp @@ -0,0 +1,50 @@ +#include "shared_icons.hpp" + +namespace nana +{ + namespace detail + { + shared_icons::shared_icons(): + path_("/usr/share/icons/"), + ifs_("/usr/share/icons/default/index.theme") + { + } + + std::string shared_icons::cursor(const std::string& name) + { + auto theme = _m_read("Icon Theme", "Inherits"); + return path_ + theme + "/cursors/" + name; + } + + std::string shared_icons::_m_read(const std::string& category, const std::string& key) + { + ifs_.seekg(0, std::ios::beg); + + bool found_cat = false; + while(ifs_.good()) + { + std::string text; + std::getline(ifs_, text); + + if(0 == text.find('[')) + { + if(found_cat) + break; + + if(text.find(category + "]") != text.npos) + { + found_cat = true; + } + } + else if(found_cat && (text.find(key + "=") == 0)) + { + return text.substr(key.size() + 1); + } + } + + return {}; + } + + } + +} \ No newline at end of file diff --git a/source/detail/posix/shared_icons.hpp b/source/detail/posix/shared_icons.hpp new file mode 100644 index 00000000..1b225fcf --- /dev/null +++ b/source/detail/posix/shared_icons.hpp @@ -0,0 +1,28 @@ +#ifndef NANA_DETAIL_SHARED_ICONS_INCLUDED +#define NANA_DETAIL_SHARED_ICONS_INCLUDED + +#include +#include + +namespace nana +{ + namespace detail + { + class shared_icons + { + public: + shared_icons(); + + std::string cursor(const std::string& name); + private: + std::string _m_read(const std::string& category, const std::string& key); + private: + std::string path_; + std::ifstream ifs_; + }; + + }//end namespace detail + +}//end namespace nana + +#endif \ No newline at end of file diff --git a/source/detail/posix/xdnd_protocol.hpp b/source/detail/posix/xdnd_protocol.hpp index d902d175..ad101c56 100644 --- a/source/detail/posix/xdnd_protocol.hpp +++ b/source/detail/posix/xdnd_protocol.hpp @@ -18,6 +18,8 @@ #include #include +#include + #include //debug @@ -25,6 +27,56 @@ namespace nana{ namespace detail { + + class shared_icons + { + public: + shared_icons() + { + path_ = "/usr/share/icons/"; + ifs_.open(path_ + "default/index.theme"); + } + + std::string cursor(const std::string& name) + { + auto theme = _m_read("Icon Theme", "Inherits"); + + return path_ + theme + "/cursors/" + name; + } + private: + std::string _m_read(const std::string& category, const std::string& key) + { + ifs_.seekg(0, std::ios::beg); + + bool found_cat = false; + while(ifs_.good()) + { + std::string text; + std::getline(ifs_, text); + + if(0 == text.find('[')) + { + if(found_cat) + break; + + if(text.find(category + "]") != text.npos) + { + found_cat = true; + } + } + else if(found_cat && (text.find(key + "=") == 0)) + { + return text.substr(key.size() + 1); + } + } + + return {}; + } + private: + std::string path_; + std::ifstream ifs_; + }; + struct xdnd_data { std::vector files; @@ -36,7 +88,8 @@ namespace nana{ enum class xdnd_status_state { normal, - position_sent, + position, + drop, //Use the 'accept' flag of XdndStatus when mouse has released(XdndDrop has been sent). status_ignore }; @@ -44,9 +97,21 @@ namespace nana{ spec_(nana::detail::platform_spec::instance()), source_(source) { + auto disp = spec_.open_display(); detail::platform_scope_guard lock; - ::XSetSelectionOwner(spec_.open_display(), spec_.atombase().xdnd_selection, source, CurrentTime); + ::XSetSelectionOwner(disp, spec_.atombase().xdnd_selection, source, CurrentTime); std::cout<<"XSetSelectionOwner "<(xclient.data.l[0]); bool is_accepted_by_target = (xclient.data.l[1] & 1); - std::cout<<"XdndStatus: Accepted="< mvout_table_; + + struct cursor_rep + { + Cursor dnd_move{ 0 }; + Cursor dnd_none{ 0 }; + }cursor_; }; //end class xdnd_protocol } } diff --git a/source/gui/dragdrop.cpp b/source/gui/dragdrop.cpp index baa4b8f3..d04e5a92 100644 --- a/source/gui/dragdrop.cpp +++ b/source/gui/dragdrop.cpp @@ -659,55 +659,6 @@ namespace nana bool const simple_mode_; std::atomic ref_count_{ 1 }; }; - - class shared_icons - { - public: - shared_icons() - { - path_ = "/usr/share/icons/"; - ifs_.open(path_ + "default/index.theme"); - } - - std::string cursor(const std::string& name) - { - auto theme = _m_read("Icon Theme", "Inherits"); - - return path_ + theme + "/cursors/" + name; - } - private: - std::string _m_read(const std::string& category, const std::string& key) - { - ifs_.seekg(0, std::ios::beg); - - bool found_cat = false; - while(ifs_.good()) - { - std::string text; - std::getline(ifs_, text); - - if(0 == text.find('[')) - { - if(found_cat) - break; - - if(text.find(category + "]") != text.npos) - { - found_cat = true; - } - } - else if(found_cat && (text.find(key + "=") == 0)) - { - return text.substr(key.size() + 1); - } - } - - return {}; - } - private: - std::string path_; - std::ifstream ifs_; - }; #endif class dragdrop_service @@ -1022,7 +973,7 @@ namespace nana #ifdef NANA_WINDOWS #elif defined (NANA_X11) - shared_icons icons_; + nana::detail::shared_icons icons_; struct hovered_status { Window native_wd{0}; diff --git a/source/gui/filebox.cpp b/source/gui/filebox.cpp index 3faaf2a1..e89d8af2 100644 --- a/source/gui/filebox.cpp +++ b/source/gui/filebox.cpp @@ -35,6 +35,7 @@ # include # include # include +# include "../detail/posix/shared_icons.hpp" #endif namespace fs = std::filesystem; diff --git a/source/gui/widgets/scroll.cpp b/source/gui/widgets/scroll.cpp index d8e7d64a..b699517a 100644 --- a/source/gui/widgets/scroll.cpp +++ b/source/gui/widgets/scroll.cpp @@ -197,6 +197,7 @@ namespace nana clr.from_rgb(0x86, 0xD5, 0xFD); break; case states::selected: clr.from_rgb(0x3C, 0x7F, 0xB1); break; + default: break; } graph.rectangle(r, false, clr); From 4f9b0560c7e4174482c2449aebae969d030b7545 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Wed, 12 Dec 2018 01:28:09 +0800 Subject: [PATCH 103/149] fix a compiler error --- include/nana/filesystem/filesystem.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/nana/filesystem/filesystem.hpp b/include/nana/filesystem/filesystem.hpp index 9d9c62f5..c17efd6d 100644 --- a/include/nana/filesystem/filesystem.hpp +++ b/include/nana/filesystem/filesystem.hpp @@ -388,7 +388,7 @@ namespace nana { namespace experimental { namespace filesystem private: bool end_{false}; path::string_type path_; - directory_options option_{ directory_options::opt }; + directory_options option_{ directory_options::none }; std::shared_ptr find_ptr_; find_handle handle_{nullptr}; From 186b76e7657ce6667a24d5e0b8df539dcdb8909a Mon Sep 17 00:00:00 2001 From: Jinhao Date: Thu, 13 Dec 2018 06:43:11 +0800 Subject: [PATCH 104/149] improve filebox appearance on Linux --- source/detail/posix/shared_icons.cpp | 50 ----- source/detail/posix/shared_icons.hpp | 28 --- source/detail/posix/theme.cpp | 298 +++++++++++++++++++++++++++ source/detail/posix/theme.hpp | 31 +++ source/gui/filebox.cpp | 111 +++++++++- source/gui/widgets/listbox.cpp | 2 +- source/gui/widgets/treebox.cpp | 9 +- 7 files changed, 441 insertions(+), 88 deletions(-) delete mode 100644 source/detail/posix/shared_icons.cpp delete mode 100644 source/detail/posix/shared_icons.hpp create mode 100644 source/detail/posix/theme.cpp create mode 100644 source/detail/posix/theme.hpp diff --git a/source/detail/posix/shared_icons.cpp b/source/detail/posix/shared_icons.cpp deleted file mode 100644 index c6345db7..00000000 --- a/source/detail/posix/shared_icons.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "shared_icons.hpp" - -namespace nana -{ - namespace detail - { - shared_icons::shared_icons(): - path_("/usr/share/icons/"), - ifs_("/usr/share/icons/default/index.theme") - { - } - - std::string shared_icons::cursor(const std::string& name) - { - auto theme = _m_read("Icon Theme", "Inherits"); - return path_ + theme + "/cursors/" + name; - } - - std::string shared_icons::_m_read(const std::string& category, const std::string& key) - { - ifs_.seekg(0, std::ios::beg); - - bool found_cat = false; - while(ifs_.good()) - { - std::string text; - std::getline(ifs_, text); - - if(0 == text.find('[')) - { - if(found_cat) - break; - - if(text.find(category + "]") != text.npos) - { - found_cat = true; - } - } - else if(found_cat && (text.find(key + "=") == 0)) - { - return text.substr(key.size() + 1); - } - } - - return {}; - } - - } - -} \ No newline at end of file diff --git a/source/detail/posix/shared_icons.hpp b/source/detail/posix/shared_icons.hpp deleted file mode 100644 index 1b225fcf..00000000 --- a/source/detail/posix/shared_icons.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef NANA_DETAIL_SHARED_ICONS_INCLUDED -#define NANA_DETAIL_SHARED_ICONS_INCLUDED - -#include -#include - -namespace nana -{ - namespace detail - { - class shared_icons - { - public: - shared_icons(); - - std::string cursor(const std::string& name); - private: - std::string _m_read(const std::string& category, const std::string& key); - private: - std::string path_; - std::ifstream ifs_; - }; - - }//end namespace detail - -}//end namespace nana - -#endif \ No newline at end of file diff --git a/source/detail/posix/theme.cpp b/source/detail/posix/theme.cpp new file mode 100644 index 00000000..417bc744 --- /dev/null +++ b/source/detail/posix/theme.cpp @@ -0,0 +1,298 @@ +#include "theme.hpp" +#include +#include +#include + +#include //debug +namespace nana +{ + namespace detail + { + static int gschema_override_priority(const std::string& filename) + { + if(filename.size() < 3) + return -1; + + auto str = filename.substr(0, 2); + if('0' <= str[0] && str[0] <= '9' && '0' <= str[1] && str[1] <= '9') + return std::stoi(str); + + return 0; + } + + //Removes the wrap of ' and " character. + std::string remove_decoration(const std::string& primitive_value) + { + auto pos = primitive_value.find_first_of("'\""); + if(pos == primitive_value.npos) + return primitive_value; + + auto endpos = primitive_value.find(primitive_value[pos], pos + 1); + if(endpos == primitive_value.npos) + return primitive_value; + + return primitive_value.substr(pos + 1, endpos - pos - 1); + } + + std::string find_value(std::ifstream& ifs, const std::string& category, const std::string& key) + { + ifs.seekg(0, std::ios::beg); + + std::string dec_categ = "[" + category + "]"; + + bool found_cat = false; + while(ifs.good()) + { + std::string text; + std::getline(ifs, text); + + if((text.size() > 2) && ('[' == text[0])) + { + if(found_cat) + break; + + found_cat = (text == dec_categ); + } + else if(found_cat && (text.find(key + "=") == 0)) + { + return remove_decoration(text.substr(key.size() + 1)); + } + } + return {}; + } + + std::vector split_value(const std::string& value_string) + { + std::vector values; + + std::size_t start_pos = 0; + while(start_pos != value_string.npos) + { + auto pos = value_string.find(',', start_pos); + if(value_string.npos == pos) + { + if(start_pos < value_string.size()) + values.emplace_back(value_string.substr(start_pos)); + break; + } + + values.emplace_back(value_string.substr(start_pos, pos - start_pos)); + + start_pos = value_string.find_first_not_of(',', pos); + } + return values; + } + + std::string find_gnome_theme_name() + { + namespace fs = std::filesystem; + try + { + //Searches all the gschema override files + std::vector overrides; + for(fs::directory_iterator i{"/usr/share/glib-2.0/schemas"}, end; i != end; ++i) + { + auto filename = i->path().filename().string(); + if(filename.size() > 17 && filename.substr(filename.size() - 17) == ".gschema.override") + { + auto priority = gschema_override_priority(filename); + if(priority < 0) + continue; + + auto i = std::find_if(overrides.cbegin(), overrides.cend(), [priority](const std::string& ovrd){ + return (priority > gschema_override_priority(ovrd)); + }); + + overrides.emplace(i, std::move(filename)); + //overrides.insert(i, filename); + } + } + + //Searches the org.gnome.desktop.interface in override files. + for(auto & gschema_override : overrides) + { + std::ifstream ifs{"/usr/share/glib-2.0/schemas/" + gschema_override}; + auto value = find_value(ifs, "org.gnome.desktop.interface", "icon-theme"); + if(!value.empty()) + return value; + } + + //Return the value from org.gnome.desktop.interface.gschema.xml + fs::path xml = "/usr/share/glib-2.0/schemas/org.gnome.desktop.interface.gschema.xml"; + auto bytes = fs::file_size(xml); + if(0 == bytes) + return {}; + + std::ifstream xml_ifs{"/usr/share/glib-2.0/schemas/org.gnome.desktop.interface.gschema.xml", std::ios::binary}; + if(xml_ifs) + { + std::string data; + data.resize(bytes); + + xml_ifs.read(&data.front(), bytes); + + auto pos = data.find("\"icon-theme\""); + if(pos != data.npos) + { + + pos = data.find("", pos + 22); + if(pos != data.npos) + { + pos += 9; + auto endpos = data.find("", pos); + if(endpos != data.npos) + { + return remove_decoration(data.substr(pos, endpos - pos)); + } + } + } + } + } + catch(...){} + + return {}; + } + + + class icon_theme + { + public: + icon_theme(const std::string& name): + theme_name_(name), + ifs_("/usr/share/icons/" + name + "/index.theme") + { + //First of all, read the Inherits and Directories + inherits_ = split_value(find_value(ifs_, "Icon Theme", "Inherits")); + directories_ = split_value(find_value(ifs_, "Icon Theme", "Directories")); + + } + + std::string find(const std::string& name, std::size_t size_wanted) const + { + namespace fs = std::filesystem; + //candidates + std::vector> first, second, third; + + fs::path theme_path = "/usr/share/icons/" + theme_name_; + + std::string base_path = "/usr/share/icons/" + theme_name_ + "/"; + std::string filename = "/" + name + ".png"; + + std::error_code err; + for(auto & dir : directories_) + { + if(!fs::exists(theme_path / dir / (name + ".png"), err)) + continue; + + auto size = find_value(ifs_, dir, "Size"); + auto type = find_value(ifs_, dir, "Type"); + auto scaled = find_value(ifs_, dir, "Scale"); + + if(size.empty() || ("Fixed" != type)) + continue; + + int int_size = std::stoi(size); + + if(!scaled.empty()) + int_size *= std::stoi(scaled); + + auto distance = std::abs(static_cast(size_wanted) - int_size); + + if(0 == distance) + { + if(scaled.empty() || scaled == "1") + return base_path + dir + filename; + else + first.emplace_back(dir, 0); + } + else + { + if(scaled.empty() || scaled == "1") + second.emplace_back(dir, distance); + else + third.emplace_back(dir, distance); + } + } + + using pair_type = std::pair; + auto comp = [](const pair_type& a, const pair_type& b){ + return a.second < b.second; + }; + + std::sort(first.begin(), first.end(), comp); + std::sort(second.begin(), second.end(), comp); + std::sort(third.begin(), third.end(), comp); + + std::string closer_dir; + if(!first.empty()) + closer_dir = first.front().first; + else if(!second.empty()) + closer_dir = second.front().first; + else if(!third.empty()) + closer_dir = third.front().first; + + + if(closer_dir.empty()) + { + for(auto & inh : inherits_) + { + auto dir = icon_theme{inh}.find(name, size_wanted); + if(!dir.empty()) + return dir; + } + + return icon_theme{"hicolor"}.find(name, size_wanted); + } + + return base_path + closer_dir + filename; + } + private: + const std::string theme_name_; + mutable std::ifstream ifs_; + std::vector inherits_; + std::vector directories_; + }; + + void test() + { + theme thm; + auto png = thm.icon("folder", 30); + std::cout<<"Icon Theme="<second) + { + if(p.first == size_wanted) + return p.second; + } + } + + //Cache is missed. + auto file = icon_theme{find_gnome_theme_name()}.find(name, size_wanted); + if(!file.empty()) + iconcache_[name].emplace_back(size_wanted, file); + + return file; + } + + } + +} \ No newline at end of file diff --git a/source/detail/posix/theme.hpp b/source/detail/posix/theme.hpp new file mode 100644 index 00000000..6b4f7a75 --- /dev/null +++ b/source/detail/posix/theme.hpp @@ -0,0 +1,31 @@ +#ifndef NANA_DETAIL_THEME_INCLUDED +#define NANA_DETAIL_THEME_INCLUDED + +#include +#include +#include +#include + +namespace nana +{ + namespace detail + { + class theme + { + public: + theme(); + + std::string cursor(const std::string& name) const; + std::string icon(const std::string& name, std::size_t size_wanted) const; + private: + std::string path_; + mutable std::ifstream ifs_; + + mutable std::map>> iconcache_; + }; + + }//end namespace detail + +}//end namespace nana + +#endif \ No newline at end of file diff --git a/source/gui/filebox.cpp b/source/gui/filebox.cpp index e89d8af2..d31ceb62 100644 --- a/source/gui/filebox.cpp +++ b/source/gui/filebox.cpp @@ -35,13 +35,12 @@ # include # include # include -# include "../detail/posix/shared_icons.hpp" +# include "../detail/posix/theme.hpp" #endif namespace fs = std::filesystem; namespace fs_ext = nana::filesystem_ext; - namespace nana { #if defined(NANA_POSIX) @@ -159,6 +158,15 @@ namespace nana pick_directory_(pick_directory), mode_(dialog_mode) { + images_.folder.open(theme_.icon("folder", 16)); + images_.file.open(theme_.icon("empty", 16)); + images_.exec.open(theme_.icon("exec", 16)); + images_.package.open(theme_.icon("package", 16)); + images_.text.open(theme_.icon("text", 16)); + images_.xml.open(theme_.icon("text-xml", 16)); + images_.image.open(theme_.icon("image", 16)); + images_.pdf.open(theme_.icon("application-pdf", 16)); + internationalization i18n; path_.create(*this); path_.splitstr("/"); @@ -215,6 +223,16 @@ namespace nana tree_.create(*this); + //Configure treebox icons + auto & fs_icons = tree_.icon("icon-fs"); + fs_icons.normal.open(theme_.icon("drive-harddisk", 16)); + + auto & folder_icons = tree_.icon("icon-folder"); + folder_icons.normal.open(theme_.icon("folder", 16)); + folder_icons.expanded.open(theme_.icon("folder-open", 16)); + + tree_.icon("icon-home").normal.open(theme_.icon("folder_home", 16)); + ls_file_.create(*this); ls_file_.append_header(i18n("NANA_FILEBOX_HEADER_NAME"), 190); ls_file_.append_header(i18n("NANA_FILEBOX_HEADER_MODIFIED"), 145); @@ -449,9 +467,13 @@ namespace nana private: void _m_layout() { + unsigned ascent, desent, ileading; + paint::graphics{nana::size{1, 1}}.text_metrics(ascent, desent, ileading); + + auto text_height = ascent + desent + 16; place_.bind(*this); place_.div( "vert" - "" + "" "" "" "" @@ -479,8 +501,10 @@ namespace nana //"FS.HOME", "FS.ROOT". Because a key of the tree widget should not be '/' nodes_.home = tree_.insert("FS.HOME", "Home"); nodes_.home.value(kind::filesystem); + nodes_.home.icon("icon-home"); nodes_.filesystem = tree_.insert("FS.ROOT", "Filesystem"); nodes_.filesystem.value(kind::filesystem); + nodes_.filesystem.icon("icon-fs"); std::vector> paths; paths.emplace_back(fs_ext::path_user().native(), nodes_.home); @@ -496,7 +520,7 @@ namespace nana continue; item_proxy node = tree_.insert(p.second, name, name); - if (false == node.empty()) + if (!node.empty()) { node.value(kind::filesystem); break; @@ -706,7 +730,70 @@ namespace nana { if(_m_filter_allowed(fs.name, fs.directory, filter, ext_types)) { - cat.append(fs).value(fs); + auto m = cat.append(fs); + m.value(fs); + + if(fs.directory) + m.icon(images_.folder); + else + { + std::string filename = fs.name; + for(auto ch : fs.name) + { + if('A' <= ch && ch <= 'Z') + ch = ch - 'A' + 'a'; + + filename += ch; + } + + auto size = filename.size(); + paint::image use_image; + + if(size > 3) + { + auto ext3 = filename.substr(size - 3); + if((".7z" == ext3) || (".ar" == ext3) || (".gz" == ext3) || (".xz" == ext3)) + use_image = images_.package; + } + + if(use_image.empty() && (size > 4)) + { + auto ext4 = filename.substr(size - 4); + + if( (".exe" == ext4) || + (".dll" == ext4)) + use_image = images_.exec; + else if((".zip" == ext4) || (".rar" == ext4) || + (".bz2" == ext4) || (".tar" == ext4)) + use_image = images_.package; + else if(".txt" == ext4) + use_image = images_.text; + else if ((".xml" == ext4) || (".htm" == ext4)) + use_image = images_.xml; + else if((".jpg" == ext4) || + (".png" == ext4) || + (".gif" == ext4) || + (".bmp" == ext4)) + use_image = images_.image; + else if(".pdf" == ext4) + use_image = images_.pdf; + } + + if(use_image.empty() && (size > 5)) + { + auto ext5 = filename.substr(size - 5); + if(".lzma" == ext5) + use_image = images_.package; + else if(".html" == ext5) + use_image = images_.xml; + } + + if(use_image.empty()) + m.icon(images_.file); + else + m.icon(use_image); + + } } } ls_file_.auto_draw(true); @@ -917,6 +1004,7 @@ namespace nana auto child = node.append(name, name, kind::filesystem); if(!child.empty()) { + child->icon("icon-folder"); //The try-catch can be eleminated by using //directory_iterator( const std::filesystem::path& p, std::error_code& ec ) noexcept; //in C++17 @@ -981,6 +1069,19 @@ namespace nana static std::string saved_init_path; static std::string saved_selected_path; + nana::detail::theme theme_; + + struct images + { + paint::image folder; + paint::image file; + paint::image exec; + paint::image package; + paint::image text; + paint::image xml; + paint::image image; + paint::image pdf; + }images_; };//end class filebox_implement std::string filebox_implement::saved_init_path; std::string filebox_implement::saved_selected_path; diff --git a/source/gui/widgets/listbox.cpp b/source/gui/widgets/listbox.cpp index eb302d9a..4f93a660 100644 --- a/source/gui/widgets/listbox.cpp +++ b/source/gui/widgets/listbox.cpp @@ -3862,7 +3862,7 @@ namespace nana { nana::rectangle imgt(item.img_show_size); img_r = imgt; - img_r.x = content_pos + coord.x + (16 - static_cast(item.img_show_size.width)) / 2; // center in 16 - geom scheme? + img_r.x = content_pos + coord.x + 2 + (16 - static_cast(item.img_show_size.width)) / 2; // center in 16 - geom scheme? img_r.y = coord.y + (static_cast(essence_->item_height()) - static_cast(item.img_show_size.height)) / 2; // center } content_pos += 18; // image width, geom scheme? diff --git a/source/gui/widgets/treebox.cpp b/source/gui/widgets/treebox.cpp index b5830180..cf5c4ea8 100644 --- a/source/gui/widgets/treebox.cpp +++ b/source/gui/widgets/treebox.cpp @@ -1522,11 +1522,12 @@ namespace nana { const nana::paint::image * img = nullptr; auto & item_attr = compset->item_attribute(); - if (item_attr.mouse_pointed) - img = &(item_attr.icon_hover); - else if (item_attr.expended) - img = &(item_attr.icon_expanded); + if (item_attr.expended) + img = &(item_attr.icon_expanded); + else if (item_attr.mouse_pointed) + img = &(item_attr.icon_hover); + if((nullptr == img) || img->empty()) img = &(item_attr.icon_normal); From 93df609520ff395d6a4d465f6b2e4d799bb9885b Mon Sep 17 00:00:00 2001 From: Jinhao Date: Fri, 14 Dec 2018 07:14:04 +0800 Subject: [PATCH 105/149] fix bug that label renders an additional endline(#365) --- source/gui/widgets/label.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/source/gui/widgets/label.cpp b/source/gui/widgets/label.cpp index 74bedf81..3095ec56 100644 --- a/source/gui/widgets/label.cpp +++ b/source/gui/widgets/label.cpp @@ -130,7 +130,7 @@ namespace nana if (!_m_foreach_visual_line(graph, rs)) break; - rs.pos.y += static_cast(rs.vslines.back().extent_height_px); + //Now the y-position of rs has been modified to next line. } if (transient_.current_font != pre_font) @@ -531,10 +531,6 @@ namespace nana bool _m_foreach_visual_line(graph_reference graph, render_status& rs) { - std::wstring text; - - content_element_iterator block_start; - auto const bottom = static_cast(graph.height()) - 1; for (auto & vsline : rs.vslines) From 36095c91908f7ea6d8b37504e13b431f7480a70f Mon Sep 17 00:00:00 2001 From: besh81 Date: Fri, 14 Dec 2018 12:38:06 +0100 Subject: [PATCH 106/149] fix disabled checkbox square disabled checkbox square now looks like disabled textbox In addition found an unused instruction in text_editor.cpp: could be replaced or removed (or left as it is :) ) --- source/gui/element.cpp | 3 ++- source/gui/widgets/checkbox.cpp | 6 +++++- source/gui/widgets/skeletons/text_editor.cpp | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/source/gui/element.cpp b/source/gui/element.cpp index a67a7341..ea3b94a2 100644 --- a/source/gui/element.cpp +++ b/source/gui/element.cpp @@ -163,7 +163,8 @@ namespace nana bld_fgcolor = fgcolor.blend(highlighted, 0.6); break; case element_state::disabled: - bld_bgcolor = bld_fgcolor = static_cast(0xb2b7bc); + bld_bgcolor = static_cast(0xE0E0E0); + bld_fgcolor = static_cast(0x999A9E); break; default: //Leave things as they are diff --git a/source/gui/widgets/checkbox.cpp b/source/gui/widgets/checkbox.cpp index c458a4ac..a43cd908 100644 --- a/source/gui/widgets/checkbox.cpp +++ b/source/gui/widgets/checkbox.cpp @@ -85,9 +85,13 @@ namespace nana{ namespace drawerbase graph.text_metrics(txt_px, descent, ileading); txt_px += (descent + 2); + auto e_state = API::element_state(*wdg); + if(!wdg->enabled()) + e_state = element_state::disabled; + impl_->crook.draw(graph, impl_->scheme_ptr->square_bgcolor.get(wdg->bgcolor()), impl_->scheme_ptr->square_border_color.get(wdg->fgcolor()), - rectangle(0, txt_px > 16 ? (txt_px - 16) / 2 : 0, 16, 16), API::element_state(*wdg)); + rectangle(0, txt_px > 16 ? (txt_px - 16) / 2 : 0, 16, 16), e_state); } void drawer::mouse_down(graph_reference graph, const arg_mouse&) diff --git a/source/gui/widgets/skeletons/text_editor.cpp b/source/gui/widgets/skeletons/text_editor.cpp index 304ed929..72d26a2e 100644 --- a/source/gui/widgets/skeletons/text_editor.cpp +++ b/source/gui/widgets/skeletons/text_editor.cpp @@ -2002,7 +2002,7 @@ namespace nana { auto fgcolor = scheme_->foreground.get_color(); if (!API::window_enabled(window_)) - fgcolor.blend(bgcolor, 0.5); + fgcolor.blend(bgcolor, 0.5); // do nothing !!! should be replace with fgcolor = fgcolor.blend(bgcolor, 0.5); <\code> or removed if (API::widget_borderless(window_)) graph_.rectangle(false, bgcolor); From fb7a16bc61c51cf3e3bba7c92953ac2744e4e507 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Sat, 15 Dec 2018 10:48:16 +0800 Subject: [PATCH 107/149] use std::filesystem instead of std::experimental::filesystem --- include/nana/filesystem/filesystem.hpp | 11 +++++- .../paint/detail/image_impl_interface.hpp | 2 +- include/nana/paint/graphics.hpp | 2 +- source/detail/platform_abstraction.cpp | 2 +- source/detail/platform_abstraction.hpp | 2 +- source/detail/posix/theme.cpp | 5 ++- source/filesystem/filesystem.cpp | 28 +++++++++++--- source/gui/dragdrop.cpp | 37 ++++++++++--------- source/paint/detail/image_bmp.hpp | 4 +- source/paint/detail/image_ico.hpp | 4 +- source/paint/detail/image_ico_resource.hpp | 2 +- source/paint/detail/image_jpeg.hpp | 2 +- source/paint/detail/image_png.hpp | 2 +- source/paint/image.cpp | 2 +- source/paint/truetype.hpp | 2 +- 15 files changed, 68 insertions(+), 39 deletions(-) diff --git a/include/nana/filesystem/filesystem.hpp b/include/nana/filesystem/filesystem.hpp index c17efd6d..c4815c94 100644 --- a/include/nana/filesystem/filesystem.hpp +++ b/include/nana/filesystem/filesystem.hpp @@ -175,7 +175,7 @@ namespace nana { namespace experimental { namespace filesystem unknown = 0xFFFF ///< not known, such as when a file_status object is created without specifying the permissions }; //enum class copy_options; - + enum class directory_options { none, @@ -538,13 +538,20 @@ namespace std { namespace filesystem { using namespace std::experimental::filesystem; -#if (defined(_MSC_VER) && ((!defined(_MSVC_LANG)) || (_MSVC_LANG < 201703))) +#if defined(NANA_FILESYSTEM_FORCE) || \ + (defined(_MSC_VER) && ((!defined(_MSVC_LANG)) || (_MSVC_LANG < 201703))) path absolute(const path& p); path absolute(const path& p, std::error_code& err); path canonical(const path& p); path canonical(const path& p, std::error_code& err); #endif + +#if defined(NANA_FILESYSTEM_FORCE) || defined(NANA_MINGW) + bool exists( std::filesystem::file_status s ) noexcept; + bool exists( const std::filesystem::path& p ); + bool exists( const std::filesystem::path& p, std::error_code& ec ) noexcept; +#endif } } // std diff --git a/include/nana/paint/detail/image_impl_interface.hpp b/include/nana/paint/detail/image_impl_interface.hpp index 4a99abaf..6a0e1141 100644 --- a/include/nana/paint/detail/image_impl_interface.hpp +++ b/include/nana/paint/detail/image_impl_interface.hpp @@ -16,7 +16,7 @@ namespace nana{ namespace paint{ public: using graph_reference = nana::paint::graphics&; virtual ~image_impl_interface() = 0; //The destructor is defined in ../image.cpp - virtual bool open(const std::experimental::filesystem::path& file) = 0; + virtual bool open(const std::filesystem::path& file) = 0; virtual bool open(const void* data, std::size_t bytes) = 0; // reads image from memory virtual bool alpha_channel() const = 0; virtual bool empty() const = 0; diff --git a/include/nana/paint/graphics.hpp b/include/nana/paint/graphics.hpp index 4eed26a6..3cec589c 100644 --- a/include/nana/paint/graphics.hpp +++ b/include/nana/paint/graphics.hpp @@ -33,7 +33,7 @@ namespace nana { friend class graphics; public: - using path_type = ::std::experimental::filesystem::path; + using path_type = ::std::filesystem::path; using font_style = ::nana::detail::font_style; diff --git a/source/detail/platform_abstraction.cpp b/source/detail/platform_abstraction.cpp index c9178c2f..515f6ace 100644 --- a/source/detail/platform_abstraction.cpp +++ b/source/detail/platform_abstraction.cpp @@ -158,7 +158,7 @@ namespace nana : public font_interface { public: - using path_type = std::experimental::filesystem::path; + using path_type = std::filesystem::path; internal_font(const path_type& ttf, const std::string& font_family, double font_size, const font_style& fs, native_font_type native_font): ttf_(ttf), diff --git a/source/detail/platform_abstraction.hpp b/source/detail/platform_abstraction.hpp index 290e0a5c..3e22112a 100644 --- a/source/detail/platform_abstraction.hpp +++ b/source/detail/platform_abstraction.hpp @@ -28,7 +28,7 @@ namespace nana public: using font = font_interface; - using path_type = ::std::experimental::filesystem::path; + using path_type = ::std::filesystem::path; static void initialize(); /// Shutdown before destruction of platform_spec diff --git a/source/detail/posix/theme.cpp b/source/detail/posix/theme.cpp index 417bc744..cebcf8f5 100644 --- a/source/detail/posix/theme.cpp +++ b/source/detail/posix/theme.cpp @@ -1,3 +1,5 @@ +#include +#if defined(NANA_POSIX) && defined(NANA_X11) #include "theme.hpp" #include #include @@ -295,4 +297,5 @@ namespace nana } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/source/filesystem/filesystem.cpp b/source/filesystem/filesystem.cpp index aa2c6ab4..a61e2f66 100644 --- a/source/filesystem/filesystem.cpp +++ b/source/filesystem/filesystem.cpp @@ -116,7 +116,7 @@ namespace nana //Windows stores file times using the FILETIME structure, which is a 64 bit value of 100ns intervals from January 1, 1601. //What's worse is that this 1601 date is fairly common to see given that it's the all zeroes value, and it is far before the //earliest date representable with time_t.std::filesystem can't change the reality of the underlying platform. - + try { #if NANA_USING_BOOST_FILESYSTEM @@ -275,7 +275,7 @@ namespace nana { namespace experimental { namespace filesystem return has_root_directory(); #endif } - + bool path::is_relative() const { return !is_absolute(); @@ -310,7 +310,7 @@ namespace nana { namespace experimental { namespace filesystem ; } - + path path::root_name() const { @@ -1123,11 +1123,12 @@ namespace nana { namespace experimental { namespace filesystem } //end namespace experimental }//end namespace nana -#if (defined(_MSC_VER) && ((!defined(_MSVC_LANG)) || (_MSVC_LANG < 201703))) namespace std { namespace filesystem { +#if defined(NANA_FILESYSTEM_FORCE) || \ + (defined(_MSC_VER) && ((!defined(_MSVC_LANG)) || (_MSVC_LANG < 201703))) path absolute(const path& p) { if (p.empty()) @@ -1236,9 +1237,26 @@ namespace std { return canonical(p, &err); } +#endif + +#if defined(NANA_FILESYSTEM_FORCE) || defined(NANA_MINGW) + bool exists( std::filesystem::file_status s ) noexcept + { + return s.type() != file_type::not_found; + } + + bool exists( const std::filesystem::path& p ) + { + return exists(status(p)); + } + + bool exists( const std::filesystem::path& p, std::error_code& ec ) noexcept + { + return exists(status(p, ec)); + } +#endif }//end namespace filesystem }//end namespace std -#endif #endif diff --git a/source/gui/dragdrop.cpp b/source/gui/dragdrop.cpp index d04e5a92..d6266e69 100644 --- a/source/gui/dragdrop.cpp +++ b/source/gui/dragdrop.cpp @@ -19,6 +19,7 @@ #include #include +#include #ifdef NANA_WINDOWS # include @@ -179,7 +180,7 @@ namespace nana win32com_drop_target(bool simple_mode): simple_mode_(simple_mode) { - + } //Implements IUnknown @@ -279,7 +280,7 @@ namespace nana //Drop the object if left button is released. if (0 == (key_state & (MK_LBUTTON))) return DRAGDROP_S_DROP; - + return S_OK; } @@ -468,7 +469,7 @@ namespace nana auto entry = find(*request_format, true); if (entry) return _m_copy_medium(pmedium, &entry->medium, &entry->format); - + return DV_E_FORMATETC; } @@ -481,10 +482,10 @@ namespace nana { if (NULL == pformatetc) return E_INVALIDARG; - + if (!(DVASPECT_CONTENT & pformatetc->dwAspect)) return DV_E_DVASPECT; - + HRESULT result = DV_E_TYMED; for (auto & entry : entries_) @@ -563,7 +564,7 @@ namespace nana { if (!(stgmed_dst && stgmed_src && fmt_src)) return E_INVALIDARG; - + switch (stgmed_src->tymed) { case TYMED_HGLOBAL: @@ -646,7 +647,7 @@ namespace nana { ++ref_count_; } - + std::size_t release() override { std::size_t val = --ref_count_; @@ -768,7 +769,7 @@ namespace nana auto& atombase = _m_spec().atombase(); auto ddrop = dynamic_cast(i->second); - + auto const native_source = reinterpret_cast(API::root(drag_wd)); { @@ -780,7 +781,7 @@ namespace nana hovered_.window_handle = nullptr; hovered_.native_wd = 0; window target_wd = 0; - + if(ddrop->simple_mode()) { @@ -816,7 +817,7 @@ namespace nana { hovered_.window_handle = cur_wd; - icon = (((drag_wd == cur_wd) || ddrop->has(drag_wd, cur_wd)) ? "dnd-move" : "dnd-none"); + icon = (((drag_wd == cur_wd) || ddrop->has(drag_wd, cur_wd)) ? "dnd-move" : "dnd-none"); } } @@ -855,9 +856,9 @@ namespace nana if(icon) { _m_free_cursor(); - hovered_.cursor = ::XcursorFilenameLoadCursor(disp, icons_.cursor(icon).c_str()); - ::XDefineCursor(disp, native_cur_wd, hovered_.cursor); - } + hovered_.cursor = ::XcursorFilenameLoadCursor(disp, icons_.cursor(icon).c_str()); + ::XDefineCursor(disp, native_cur_wd, hovered_.cursor); + } #endif } else if(msg_pkt.u.xevent.type == ButtonRelease) @@ -868,7 +869,7 @@ namespace nana API::release_capture(drag_wd); return detail::propagation_chain::exit; } - + } return detail::propagation_chain::stop; }); @@ -909,7 +910,7 @@ namespace nana { std::cout<<"ButtonRelease"< predicate_fn) { impl_->predicate = predicate_fn; @@ -1277,4 +1278,4 @@ namespace nana { real_data_->files.emplace_back(std::move(path)); } -}//end namespace nana \ No newline at end of file +}//end namespace nana diff --git a/source/paint/detail/image_bmp.hpp b/source/paint/detail/image_bmp.hpp index e3893c61..ac25812e 100644 --- a/source/paint/detail/image_bmp.hpp +++ b/source/paint/detail/image_bmp.hpp @@ -111,11 +111,11 @@ namespace nana{ namespace paint return true; } - bool open(const std::experimental::filesystem::path& filename) override + bool open(const std::filesystem::path& filename) override { std::ifstream ifs(filename.string(), std::ios::binary); - auto const bytes = static_cast(std::experimental::filesystem::file_size(filename)); + auto const bytes = static_cast(std::filesystem::file_size(filename)); if (ifs && (bytes > static_cast(sizeof(bitmap_file_header)))) { std::unique_ptr buffer{ new char[bytes] }; diff --git a/source/paint/detail/image_ico.hpp b/source/paint/detail/image_ico.hpp index 4f78cad6..ffa4d693 100644 --- a/source/paint/detail/image_ico.hpp +++ b/source/paint/detail/image_ico.hpp @@ -241,7 +241,7 @@ public: #endif } - bool open(const std::experimental::filesystem::path& ico_file) override + bool open(const std::filesystem::path& ico_file) override { std::ifstream file(ico_file.string(), std::ios::binary); if (!file.is_open()) return false; @@ -290,7 +290,7 @@ public: #endif } private: - std::experimental::filesystem::path path_; + std::filesystem::path path_; #if defined(NANA_WINDOWS) void* native_handle_{nullptr}; #endif diff --git a/source/paint/detail/image_ico_resource.hpp b/source/paint/detail/image_ico_resource.hpp index dd451bf0..f7e85818 100644 --- a/source/paint/detail/image_ico_resource.hpp +++ b/source/paint/detail/image_ico_resource.hpp @@ -29,7 +29,7 @@ namespace nana{ namespace paint :public image::image_impl_interface { public: - bool open(const std::experimental::filesystem::path& filename) override + bool open(const std::filesystem::path& filename) override { #if defined(NANA_WINDOWS) SHFILEINFO sfi; diff --git a/source/paint/detail/image_jpeg.hpp b/source/paint/detail/image_jpeg.hpp index d63a2c1d..0b1ecd2c 100644 --- a/source/paint/detail/image_jpeg.hpp +++ b/source/paint/detail/image_jpeg.hpp @@ -47,7 +47,7 @@ namespace nana } } public: - bool open(const std::experimental::filesystem::path& jpeg_file) override + bool open(const std::filesystem::path& jpeg_file) override { auto fp = ::fopen(to_osmbstr(to_utf8(jpeg_file.native())).c_str(), "rb"); if(nullptr == fp) return false; diff --git a/source/paint/detail/image_png.hpp b/source/paint/detail/image_png.hpp index f3cff150..91dd8e2b 100644 --- a/source/paint/detail/image_png.hpp +++ b/source/paint/detail/image_png.hpp @@ -123,7 +123,7 @@ namespace nana delete[] row_ptrs; } public: - bool open(const std::experimental::filesystem::path& png_file) override + bool open(const std::filesystem::path& png_file) override { auto fp = ::fopen(to_osmbstr(to_utf8(png_file.native())).c_str(), "rb"); if(nullptr == fp) return false; diff --git a/source/paint/image.cpp b/source/paint/image.cpp index 1781a657..9d19a46c 100644 --- a/source/paint/image.cpp +++ b/source/paint/image.cpp @@ -38,7 +38,7 @@ #include "detail/image_ico_resource.hpp" #include "detail/image_ico.hpp" -namespace fs = std::experimental::filesystem; +namespace fs = std::filesystem; namespace nana { diff --git a/source/paint/truetype.hpp b/source/paint/truetype.hpp index 5b94cd34..d8ce4b9b 100644 --- a/source/paint/truetype.hpp +++ b/source/paint/truetype.hpp @@ -47,7 +47,7 @@ namespace nana std::uint16_t string_offset; //from start of storage area }; public: - using path_type = ::std::experimental::filesystem::path; + using path_type = ::std::filesystem::path; truetype(const path_type& filename) { From 4e4fbbde46d6c80c94810a3e8de346e8a52bd850 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Sat, 15 Dec 2018 10:53:33 +0800 Subject: [PATCH 108/149] fix compiling error --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 896e4134..e9252dce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,6 +59,7 @@ set(NANA_SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/source) set(NANA_SOURCE_SUBDIRS /. /detail + /detail/posix /filesystem /gui /gui/detail From ddc38c399898e5d510c56e49a443c00ab1ed430d Mon Sep 17 00:00:00 2001 From: Shamari Campbell Date: Sun, 16 Dec 2018 23:43:39 +0000 Subject: [PATCH 109/149] Issue 362 Here are some small changes from CPPCheck and also in some places I seen that some parameters could be declared const as they are not being changed. --- include/nana/datetime.hpp | 6 +++--- include/nana/gui/detail/drawer.hpp | 2 +- include/nana/gui/effects.hpp | 2 +- source/datetime.cpp | 13 ++++++++----- source/gui/detail/drawer.cpp | 9 +++++---- 5 files changed, 18 insertions(+), 14 deletions(-) diff --git a/include/nana/datetime.hpp b/include/nana/datetime.hpp index b6678226..3abe9500 100644 --- a/include/nana/datetime.hpp +++ b/include/nana/datetime.hpp @@ -27,7 +27,7 @@ namespace nana }; date(); ///< the initialized date is today. - date(const std::tm&); + explicit date(const std::tm&); date(int year, int month, int day); date operator - (int off) const; @@ -44,8 +44,8 @@ namespace nana void set(const std::tm&); static int day_of_week(int year, int month, int day); - static unsigned year_days(unsigned year); ///< the number of days in the specified year. - static unsigned month_days(unsigned year, unsigned month); ///< the number of days in the specified month. + static unsigned year_days(const unsigned year); ///< the number of days in the specified year. + static unsigned month_days(const unsigned year, const unsigned month); ///< the number of days in the specified month. static unsigned day_in_year(unsigned y, unsigned m, unsigned d); ///< Returns the index of the specified day in this year, at range[1, 365] or [1, 366] private: date _m_add(unsigned x) const; diff --git a/include/nana/gui/detail/drawer.hpp b/include/nana/gui/detail/drawer.hpp index 63cea67f..7c20db17 100644 --- a/include/nana/gui/detail/drawer.hpp +++ b/include/nana/gui/detail/drawer.hpp @@ -91,7 +91,7 @@ namespace nana virtual void shortkey(graph_reference, const arg_keyboard&); void filter_event(const event_code evt_code, const bool bDisabled); - void filter_event(const std::vector evt_codes, const bool bDisabled); + void filter_event(const std::vector& evt_codes, const bool bDisabled); void filter_event(const event_filter_status& evt_all_states); bool filter_event(const event_code evt_code); event_filter_status filter_event(); diff --git a/include/nana/gui/effects.hpp b/include/nana/gui/effects.hpp index 3825c07a..c7a8f0a4 100644 --- a/include/nana/gui/effects.hpp +++ b/include/nana/gui/effects.hpp @@ -45,7 +45,7 @@ namespace nana : public bground_factory_interface { public: - bground_transparent(std::size_t percent); + explicit bground_transparent(std::size_t percent); private: bground_interface* create() const override; private: diff --git a/source/datetime.cpp b/source/datetime.cpp index d633090d..9911dd1d 100644 --- a/source/datetime.cpp +++ b/source/datetime.cpp @@ -15,6 +15,7 @@ #include #endif #include +#include namespace { std::tm localtime() @@ -239,18 +240,20 @@ namespace nana return days + d; } - unsigned date::month_days(unsigned year, unsigned month) + unsigned date::month_days(const unsigned year, const unsigned month) { - unsigned num[] = {31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - if(month != 2) - return num[month - 1]; + if (month != 2) + { + const std::array days_in_month = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + return days_in_month[month - 1]; + } if(((year % 4 == 0) && (year % 100)) || (year % 400 == 0)) return 29; return 28; } - unsigned date::year_days(unsigned year) + unsigned date::year_days(const unsigned year) { if(((year % 4 == 0) && (year % 100)) || (year % 400 == 0)) return 366; diff --git a/source/gui/detail/drawer.cpp b/source/gui/detail/drawer.cpp index e6b9108a..08e31cba 100644 --- a/source/gui/detail/drawer.cpp +++ b/source/gui/detail/drawer.cpp @@ -136,11 +136,12 @@ namespace nana evt_disabled_ &= ~(1 << static_cast(evt_code)); // clear } - void drawer_trigger::filter_event(const std::vector evt_codes, const bool bDisabled) + void drawer_trigger::filter_event(const std::vector& evt_codes, const bool bDisabled) { - const auto it_end = evt_codes.end(); - for (auto it = evt_codes.begin(); it != it_end; it++) - filter_event(*it, bDisabled); + for (const auto& evt_code : evt_codes) + { + filter_event(evt_code, bDisabled); + } } void drawer_trigger::filter_event(const event_filter_status& evt_all_states) From 240f1b7e319f5673bfd7e340dc9b8a3117c4acd0 Mon Sep 17 00:00:00 2001 From: Shamari Campbell Date: Mon, 17 Dec 2018 21:00:40 +0000 Subject: [PATCH 110/149] Additional change due a comment, changing 'const' to 'constexpr' #362 --- source/datetime.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/datetime.cpp b/source/datetime.cpp index 9911dd1d..8b024735 100644 --- a/source/datetime.cpp +++ b/source/datetime.cpp @@ -244,7 +244,7 @@ namespace nana { if (month != 2) { - const std::array days_in_month = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + constexpr std::array days_in_month = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; return days_in_month[month - 1]; } From 1d2c7f85f484cee0a387794f08cd869ed5e449a6 Mon Sep 17 00:00:00 2001 From: James Bremner Date: Tue, 18 Dec 2018 17:05:45 -0500 Subject: [PATCH 111/149] Partial implementation of missing nana::typeface() --- include/nana/gui/widgets/group.hpp | 9 +- source/gui/widgets/group.cpp | 480 +++++++++++++++-------------- 2 files changed, 254 insertions(+), 235 deletions(-) diff --git a/include/nana/gui/widgets/group.hpp b/include/nana/gui/widgets/group.hpp index ba48928b..c453c168 100644 --- a/include/nana/gui/widgets/group.hpp +++ b/include/nana/gui/widgets/group.hpp @@ -3,8 +3,8 @@ * Nana C++ Library(http://www.nanaro.org) * 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 + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) * * @file: nana/gui/widgets/group.hpp @@ -77,6 +77,9 @@ namespace nana{ /// Determines whether a specified option is checked, it throws an out_of_range if !(pos < number of options) bool option_checked(std::size_t pos) const; + /// Change typeface of caption label ( does not effect child widgets ) + void typeface( const nana::paint::font& font ); + group& enable_format_caption(bool format); group& collocate() noexcept; @@ -86,7 +89,7 @@ namespace nana{ void field_display(const char* field_name, bool display); /// Widget* create_child(const char* field, Args && ... args) { diff --git a/source/gui/widgets/group.cpp b/source/gui/widgets/group.cpp index 6d697a62..1f3d24dd 100644 --- a/source/gui/widgets/group.cpp +++ b/source/gui/widgets/group.cpp @@ -3,8 +3,8 @@ * Nana C++ Library(http://www.nanaro.org) * 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 + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) * * @file: nana/gui/widgets/group.cpp @@ -14,7 +14,7 @@ * @brief group is a widget used to visually group and layout other widgets. * * @contributor: - * dankan1890(https://github.com/dankan1890) + * dankan1890(https://github.com/dankan1890) */ @@ -27,300 +27,316 @@ if(empty()) \ throw std::logic_error("the group is invalid"); -namespace nana{ +namespace nana +{ - static const char* field_title = "__nana_group_title__"; - static const char* field_options = "__nana_group_options__"; +static const char* field_title = "__nana_group_title__"; +static const char* field_options = "__nana_group_options__"; - struct group::implement - { - label caption; - align caption_align{ align::left }; - place place_content; - unsigned gap{2}; - std::string usr_div_str; +struct group::implement +{ + label caption; + align caption_align{ align::left }; + place place_content; + unsigned gap{2}; + std::string usr_div_str; - nana::size caption_dimension; + nana::size caption_dimension; - std::vector> options; - radio_group * radio_logic{nullptr}; + std::vector> options; + radio_group * radio_logic{nullptr}; - implement() = default; + implement() = default; - implement(window grp_panel, ::std::string titel, bool vsb, unsigned gap=2) - : caption (grp_panel, std::move(titel), vsb), - place_content{grp_panel}, - gap{gap} - { - } + implement(window grp_panel, ::std::string titel, bool vsb, unsigned gap=2) + : caption (grp_panel, std::move(titel), vsb), + place_content{grp_panel}, + gap{gap} + { + } - void create(window pnl) - { - caption.create(pnl); - caption.caption(""); - place_content.bind(pnl); + void create(window pnl) + { + caption.create(pnl); + caption.caption(""); + place_content.bind(pnl); - if (!radio_logic) - radio_logic = new radio_group; - } + if (!radio_logic) + radio_logic = new radio_group; + } - void update_div() - { - const std::size_t padding = 10; - caption_dimension = caption.measure(1000); - caption_dimension.width += 1; + void update_div() + { + const std::size_t padding = 10; + caption_dimension = caption.measure(1000); + caption_dimension.width += 1; - std::string div = "vert margin=[0," + std::to_string(gap) + "," + std::to_string(gap + 5) + "," + std::to_string(gap) + "]"; + std::string div = "vert margin=[0," + std::to_string(gap) + "," + std::to_string(gap + 5) + "," + std::to_string(gap) + "]"; - div += ""; - else - div += "<>"; //right or center + if (align::left == caption_align) + div += ""; + else + div += "<>"; //right or center - div += "<" + std::string{ field_title } + " weight=" + std::to_string(caption_dimension.width) + ">"; + div += "<" + std::string{ field_title } + " weight=" + std::to_string(caption_dimension.width) + ">"; - if (align::right == caption_align) - div += ""; - else if (align::center == caption_align) - div += "<>"; + if (align::right == caption_align) + div += ""; + else if (align::center == caption_align) + div += "<>"; - div += "><"; + div += "><"; - if (!usr_div_str.empty()) - div += "<" + usr_div_str + ">>"; - else - div += ">"; + if (!usr_div_str.empty()) + div += "<" + usr_div_str + ">>"; + else + div += ">"; - place_content.div(div.c_str()); + place_content.div(div.c_str()); - if (options.empty()) - place_content.field_display(field_options, false); + if (options.empty()) + place_content.field_display(field_options, false); - if (caption.caption().empty()) - place_content.field_display(field_title, false); - } - }; + if (caption.caption().empty()) + place_content.field_display(field_title, false); + } +}; - group::group() - : impl_(new implement) - { - } +group::group() + : impl_(new implement) +{ +} - group::group(window parent, const rectangle& r, bool vsb) - : group() - { - create(parent, r, vsb); - } +group::group(window parent, const rectangle& r, bool vsb) + : group() +{ + create(parent, r, vsb); +} - using groupbase_type = widget_object; +using groupbase_type = widget_object; - group::group(window parent, ::std::string titel, bool formatted, unsigned gap, const rectangle& r, bool vsb) - : group(parent, r, vsb) - { - this->bgcolor(API::bgcolor(parent)); +group::group(window parent, ::std::string titel, bool formatted, unsigned gap, const rectangle& r, bool vsb) + : group(parent, r, vsb) +{ + this->bgcolor(API::bgcolor(parent)); - impl_.reset(new implement(*this, std::move(titel), vsb, gap)); + impl_.reset(new implement(*this, std::move(titel), vsb, gap)); - impl_->caption.format(formatted); - _m_init(); - } + impl_->caption.format(formatted); + _m_init(); +} - group::~group() - { - delete impl_->radio_logic; - } +group::~group() +{ + delete impl_->radio_logic; +} - checkbox& group::add_option(std::string text) - { - _THROW_IF_EMPTY() +checkbox& group::add_option(std::string text) +{ + _THROW_IF_EMPTY() #ifdef _nana_std_has_emplace_return_type - auto & opt = impl_->options.emplace_back(new checkbox{ handle() }); + auto & opt = impl_->options.emplace_back(new checkbox { handle() }); #else - impl_->options.emplace_back(new checkbox(handle())); - auto & opt = impl_->options.back(); + impl_->options.emplace_back(new checkbox(handle())); + auto & opt = impl_->options.back(); #endif - opt->transparent(true); - opt->caption(std::move(text)); - impl_->place_content[field_options] << *opt; - impl_->place_content.field_display(field_options, true); - impl_->place_content.collocate(); + opt->transparent(true); + opt->caption(std::move(text)); + impl_->place_content[field_options] << *opt; + impl_->place_content.field_display(field_options, true); + impl_->place_content.collocate(); - if (impl_->radio_logic) - impl_->radio_logic->add(*opt); + if (impl_->radio_logic) + impl_->radio_logic->add(*opt); - return *impl_->options.back(); - } + return *impl_->options.back(); +} - void group::caption_align(align position) - { - if (position != impl_->caption_align) - { - impl_->caption_align = position; - impl_->update_div(); - impl_->place_content.collocate(); - API::refresh_window(*this); - } - } +void group::caption_align(align position) +{ + if (position != impl_->caption_align) + { + impl_->caption_align = position; + impl_->update_div(); + impl_->place_content.collocate(); + API::refresh_window(*this); + } +} - group& group::radio_mode(bool enable) - { - _THROW_IF_EMPTY() +group& group::radio_mode(bool enable) +{ + _THROW_IF_EMPTY() - if (enable) - { - //Create radio_group if it is null - if (!impl_->radio_logic) - impl_->radio_logic = new ::nana::radio_group; + if (enable) + { + //Create radio_group if it is null + if (!impl_->radio_logic) + impl_->radio_logic = new ::nana::radio_group; - //add all options into the radio_group - for (auto & opt : impl_->options) - impl_->radio_logic->add(*opt); - } - else - { - delete impl_->radio_logic; - impl_->radio_logic = nullptr; - } - return *this; - } + //add all options into the radio_group + for (auto & opt : impl_->options) + impl_->radio_logic->add(*opt); + } + else + { + delete impl_->radio_logic; + impl_->radio_logic = nullptr; + } + return *this; +} - std::size_t group::option() const - { - _THROW_IF_EMPTY(); +std::size_t group::option() const +{ + _THROW_IF_EMPTY(); - if (impl_->radio_logic) - return impl_->radio_logic->checked(); + if (impl_->radio_logic) + return impl_->radio_logic->checked(); - throw std::logic_error("the radio_mode of the group is disabled"); - } + throw std::logic_error("the radio_mode of the group is disabled"); +} - bool group::option_checked(std::size_t pos) const - { - _THROW_IF_EMPTY(); - return impl_->options.at(pos)->checked(); - } +bool group::option_checked(std::size_t pos) const +{ + _THROW_IF_EMPTY(); + return impl_->options.at(pos)->checked(); +} - group& group::enable_format_caption(bool format) - { - impl_->caption.format(format); - return *this; - } +void group::typeface( const nana::paint::font& font ) +{ + // change typeface of caption label + impl_->caption.typeface( font ); - group& group::collocate() noexcept - { - impl_->place_content.collocate(); - return *this; - } + /* change size of caption label - group& group::div(const char* div_str) noexcept - { - if (div_str) - impl_->usr_div_str = div_str; - else - impl_->usr_div_str.clear(); + The caption may be changed AFTER this call + so the neccessary label size is unknown + set it to 80% of the current widget width and 50 pixels + */ + impl_->caption.move( rectangle(0,0,size().width * 0.8,50)); +} - impl_->update_div(); - return *this; - } +group& group::enable_format_caption(bool format) +{ + impl_->caption.format(format); + return *this; +} - group::field_reference group::operator[](const char* field) - { - return impl_->place_content.field(field); - } +group& group::collocate() noexcept +{ + impl_->place_content.collocate(); + return *this; +} - void group::field_display(const char* field_name, bool display) - { - impl_->place_content.field_display(field_name, display); - } +group& group::div(const char* div_str) noexcept +{ + if (div_str) + impl_->usr_div_str = div_str; + else + impl_->usr_div_str.clear(); - bool group::field_display(const char* field_name) const - { - return impl_->place_content.field_display(field_name); - } + impl_->update_div(); + return *this; +} - void group::erase(window handle) - { - impl_->place_content.erase(handle); - } +group::field_reference group::operator[](const char* field) +{ + return impl_->place_content.field(field); +} - void group::_m_add_child(const char* field, widget* wdg) - { - impl_->place_content[field] << wdg->handle(); - } +void group::field_display(const char* field_name, bool display) +{ + impl_->place_content.field_display(field_name, display); +} - void group::_m_init() - { - this->div(nullptr); +bool group::field_display(const char* field_name) const +{ + return impl_->place_content.field_display(field_name); +} - auto & outter = impl_->place_content; +void group::erase(window handle) +{ + impl_->place_content.erase(handle); +} - outter[field_title] << impl_->caption; - outter.collocate(); +void group::_m_add_child(const char* field, widget* wdg) +{ + impl_->place_content[field] << wdg->handle(); +} - impl_->caption.transparent(true); - color pbg = API::bgcolor(this->parent()); - impl_->caption.bgcolor(pbg.blend(colors::black, 0.025)); +void group::_m_init() +{ + this->div(nullptr); - this->bgcolor(pbg.blend(colors::black, 0.05)); + auto & outter = impl_->place_content; - drawing dw(*this); + outter[field_title] << impl_->caption; + outter.collocate(); - //When the group is resized, the drawing is called before moving the caption, but - //the drawing of group requires the lastest position of caption for gradual rectangle. - //For the requirement, a move event handler is required for listning the change of caption's position. - impl_->caption.events().move([this](const arg_move&){ - if (align::left != impl_->caption_align) - API::refresh_window(*this); - }); + impl_->caption.transparent(true); + color pbg = API::bgcolor(this->parent()); + impl_->caption.bgcolor(pbg.blend(colors::black, 0.025)); - // This drawing function is owner by the onwer of dw (the outer panel of the group widget), not by dw !! - dw.draw([this](paint::graphics& graph) - { - auto gap_px = impl_->gap - 1; + this->bgcolor(pbg.blend(colors::black, 0.05)); - auto const top_round_line = static_cast(impl_->caption_dimension.height) / 2; + drawing dw(*this); - graph.rectangle(true, API::bgcolor(this->parent())); - 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, this->scheme().border, true, this->bgcolor()); + //When the group is resized, the drawing is called before moving the caption, but + //the drawing of group requires the lastest position of caption for gradual rectangle. + //For the requirement, a move event handler is required for listning the change of caption's position. + impl_->caption.events().move([this](const arg_move&) + { + if (align::left != impl_->caption_align) + API::refresh_window(*this); + }); - auto opt_r = API::window_rectangle(impl_->caption); - if (opt_r) - { - rectangle grad_r{ opt_r->position(), nana::size{ opt_r->width + 4, static_cast(top_round_line - opt_r->y) } }; + // This drawing function is owner by the onwer of dw (the outer panel of the group widget), not by dw !! + dw.draw([this](paint::graphics& graph) + { + auto gap_px = impl_->gap - 1; - grad_r.y += top_round_line*2 / 3; - grad_r.x -= 2; + auto const top_round_line = static_cast(impl_->caption_dimension.height) / 2; - graph.gradual_rectangle(grad_r, - API::bgcolor(this->parent()), this->bgcolor(), true - ); - } - }); - } + graph.rectangle(true, API::bgcolor(this->parent())); + 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, this->scheme().border, true, this->bgcolor()); - void group::_m_complete_creation() - { - widget::_m_complete_creation(); - impl_->create(handle()); - _m_init(); - } + auto opt_r = API::window_rectangle(impl_->caption); + if (opt_r) + { + rectangle grad_r{ opt_r->position(), nana::size{ opt_r->width + 4, static_cast(top_round_line - opt_r->y) } }; - auto group::_m_caption() const noexcept -> native_string_type - { - return impl_->caption.caption_native(); - } + grad_r.y += top_round_line*2 / 3; + grad_r.x -= 2; - void group::_m_caption(native_string_type&& str) - { - impl_->caption.caption(std::move(str)); - impl_->update_div(); - impl_->place_content.collocate(); - } + graph.gradual_rectangle(grad_r, + API::bgcolor(this->parent()), this->bgcolor(), true + ); + } + }); +} + +void group::_m_complete_creation() +{ + widget::_m_complete_creation(); + impl_->create(handle()); + _m_init(); +} + +auto group::_m_caption() const noexcept -> native_string_type +{ + return impl_->caption.caption_native(); +} + +void group::_m_caption(native_string_type&& str) +{ + impl_->caption.caption(std::move(str)); + impl_->update_div(); + impl_->place_content.collocate(); +} }//end namespace nana From 01ed1d13e942c8865ca85d0ffe6920b6ce911480 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Thu, 20 Dec 2018 07:03:24 +0800 Subject: [PATCH 112/149] add icon theme for KDE --- source/detail/posix/theme.cpp | 43 +++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/source/detail/posix/theme.cpp b/source/detail/posix/theme.cpp index cebcf8f5..2eec4797 100644 --- a/source/detail/posix/theme.cpp +++ b/source/detail/posix/theme.cpp @@ -85,9 +85,9 @@ namespace nana return values; } + namespace fs = std::filesystem; std::string find_gnome_theme_name() { - namespace fs = std::filesystem; try { //Searches all the gschema override files @@ -156,6 +156,41 @@ namespace nana } + std::string find_kde_theme_name() + { + auto home = getenv("HOME"); + if(home) + { + fs::path kdeglobals{home}; + kdeglobals /= ".kde/share/config/kdeglobals"; + + std::error_code err; + if(fs::exists(kdeglobals, err)) + { + std::ifstream ifs{kdeglobals}; + return find_value(ifs, "Icons", "Theme"); + } + } + return {}; + } + + std::string find_theme_name() + { + auto name = find_kde_theme_name(); + + if(name.empty()) + { + name = find_gnome_theme_name(); + std::cout<<"GNOME:"< Date: Thu, 27 Dec 2018 07:29:11 +0800 Subject: [PATCH 113/149] improve dnd interfaces --- include/nana/gui/dragdrop.hpp | 37 ++++- source/detail/platform_spec_posix.cpp | 2 + source/detail/posix/platform_spec.hpp | 2 + source/detail/posix/xdnd_protocol.hpp | 188 ++++++++++++-------------- source/gui/dragdrop.cpp | 173 +++++++++++++----------- 5 files changed, 213 insertions(+), 189 deletions(-) diff --git a/include/nana/gui/dragdrop.hpp b/include/nana/gui/dragdrop.hpp index d426d34c..a20c7bb8 100644 --- a/include/nana/gui/dragdrop.hpp +++ b/include/nana/gui/dragdrop.hpp @@ -22,6 +22,14 @@ namespace nana { + /// Drag and drop actions + enum class dnd_action + { + copy, ///< Copy the data to target. + move, ///< Move the data to target. + link ///< Create a link from source data to target. + }; + class simple_dragdrop { struct implementation; @@ -67,7 +75,12 @@ namespace nana data(const data&) = delete; data& operator=(const data&) = delete; public: - data(); + /// Constructor + /** + * Constructs a data object used for drag and drop + * @param requested_action Indicates how the data to be transferred. + */ + data(dnd_action requested_action = dnd_action::copy); data(data&&); ~data(); @@ -81,9 +94,29 @@ namespace nana dragdrop(window source); ~dragdrop(); + /// Condition of dragging + /*** + * The preciate function is called when press mouse button on the source window, it returns true to indicate the start of dragging. If the predicate is not set, it always start to drag. + * @param predicate_fn A predicate function to be set. + */ void condition(std::function predicate_fn); + + /// Transferred data + /** + * Set a data generator. When drag begins, it is called to generate a data object for transferring. + * @param generator It returns the data for transferring. + */ void prepare_data(std::function generator); - void drop_finished(std::function finish_fn); + + /// Drop handler + /** + * The drop handler is called when the drop operation is completed. There are 3 parameters for the handler + * dropped Indicates whether the data is accepted by a target window. + * executed_action Indicates the action returned by target window. Ignore if dropped is false. + * data_transferred The data object which is generated by the generator. + * @param finish_fn The drop handling function. + */ + void drop_finished(std::function finish_fn); private: implementation* const impl_; }; diff --git a/source/detail/platform_spec_posix.cpp b/source/detail/platform_spec_posix.cpp index f03f0b09..f607e9a3 100644 --- a/source/detail/platform_spec_posix.cpp +++ b/source/detail/platform_spec_posix.cpp @@ -513,6 +513,8 @@ namespace detail atombase_.xdnd_position = ::XInternAtom(display_, "XdndPosition", False); atombase_.xdnd_status = ::XInternAtom(display_, "XdndStatus", False); atombase_.xdnd_action_copy = ::XInternAtom(display_, "XdndActionCopy", False); + atombase_.xdnd_action_move = ::XInternAtom(display_, "XdndActionMove", False); + atombase_.xdnd_action_link = ::XInternAtom(display_, "XdndActionLink", False); atombase_.xdnd_drop = ::XInternAtom(display_, "XdndDrop", False); atombase_.xdnd_selection = ::XInternAtom(display_, "XdndSelection", False); atombase_.xdnd_typelist = ::XInternAtom(display_, "XdndTypeList", False); diff --git a/source/detail/posix/platform_spec.hpp b/source/detail/posix/platform_spec.hpp index 0cf4b086..84c1c24e 100644 --- a/source/detail/posix/platform_spec.hpp +++ b/source/detail/posix/platform_spec.hpp @@ -159,6 +159,8 @@ namespace detail Atom xdnd_position; Atom xdnd_status; Atom xdnd_action_copy; + Atom xdnd_action_move; + Atom xdnd_action_link; Atom xdnd_drop; Atom xdnd_selection; Atom xdnd_typelist; diff --git a/source/detail/posix/xdnd_protocol.hpp b/source/detail/posix/xdnd_protocol.hpp index ad101c56..1883633e 100644 --- a/source/detail/posix/xdnd_protocol.hpp +++ b/source/detail/posix/xdnd_protocol.hpp @@ -16,69 +16,26 @@ #include "platform_spec.hpp" #include -#include +#include "theme.hpp" #include +#include + +#define DEBUG_XDND_PROTOCOL + +#ifdef DEBUG_XDND_PROTOCOL #include //debug +#endif namespace nana{ namespace detail { - - class shared_icons - { - public: - shared_icons() - { - path_ = "/usr/share/icons/"; - ifs_.open(path_ + "default/index.theme"); - } - - std::string cursor(const std::string& name) - { - auto theme = _m_read("Icon Theme", "Inherits"); - - return path_ + theme + "/cursors/" + name; - } - private: - std::string _m_read(const std::string& category, const std::string& key) - { - ifs_.seekg(0, std::ios::beg); - - bool found_cat = false; - while(ifs_.good()) - { - std::string text; - std::getline(ifs_, text); - - if(0 == text.find('[')) - { - if(found_cat) - break; - - if(text.find(category + "]") != text.npos) - { - found_cat = true; - } - } - else if(found_cat && (text.find(key + "=") == 0)) - { - return text.substr(key.size() + 1); - } - } - - return {}; - } - private: - std::string path_; - std::ifstream ifs_; - }; - struct xdnd_data { + Atom requested_action; std::vector files; }; @@ -100,31 +57,40 @@ namespace nana{ auto disp = spec_.open_display(); detail::platform_scope_guard lock; ::XSetSelectionOwner(disp, spec_.atombase().xdnd_selection, source, CurrentTime); - std::cout<<"XSetSelectionOwner "<(xclient.data.l[0]); bool is_accepted_by_target = (xclient.data.l[1] & 1); - std::cout<<"XdndStatus: Accepted="< mvout_table_; struct cursor_rep { + Cursor dnd_copy{ 0 }; Cursor dnd_move{ 0 }; Cursor dnd_none{ 0 }; }cursor_; diff --git a/source/gui/dragdrop.cpp b/source/gui/dragdrop.cpp index d6266e69..2c2d394a 100644 --- a/source/gui/dragdrop.cpp +++ b/source/gui/dragdrop.cpp @@ -39,17 +39,31 @@ namespace nana { struct dragdrop_data { + dnd_action requested_action; std::vector files; - }; #ifdef NANA_X11 - xdnd_data to_xdnd_data(const dragdrop_data& data) - { - xdnd_data xdata; - xdata.files = data.files; - return xdata; - } + xdnd_data to_xdnd_data() const noexcept + { + auto & atombase = nana::detail::platform_spec::instance().atombase(); + xdnd_data xdata; + xdata.requested_action = atombase.xdnd_action_copy; + + switch(requested_action) + { + case dnd_action::copy: + xdata.requested_action = atombase.xdnd_action_copy; break; + case dnd_action::move: + xdata.requested_action = atombase.xdnd_action_move; break; + case dnd_action::link: + xdata.requested_action = atombase.xdnd_action_link; break; + } + + xdata.files = files; + return xdata; + } #endif + }; } @@ -744,7 +758,7 @@ namespace nana } } - bool dragdrop(window drag_wd, dropdata_type* dropdata) + bool dragdrop(window drag_wd, dropdata_type* dropdata, dnd_action* executed_action) { auto i = table_.find(API::root(drag_wd)); if ((!dropdata) && table_.end() == i) @@ -764,6 +778,19 @@ namespace nana delete drop_src; + if (executed_action) + { + switch (result_effect) + { + case DROPEFFECT_COPY: + *executed_action = dnd_action::copy; break; + case DROPEFFECT_MOVE: + *executed_action = dnd_action::move; break; + case DROPEFFECT_LINK: + *executed_action = dnd_action::link; break; + } + } + return (DROPEFFECT_NONE != result_effect); #elif defined(NANA_X11) auto& atombase = _m_spec().atombase(); @@ -780,12 +807,14 @@ namespace nana hovered_.window_handle = nullptr; hovered_.native_wd = 0; - window target_wd = 0; + + if(executed_action) + *executed_action = dropdata->data()->requested_action; if(ddrop->simple_mode()) { - _m_spec().msg_dispatch([this, ddrop, drag_wd, native_source, &target_wd, &atombase](const detail::msg_packet_tag& msg_pkt) mutable{ + _m_spec().msg_dispatch([this, ddrop, drag_wd, native_source, &atombase](const detail::msg_packet_tag& msg_pkt) mutable{ if(detail::msg_packet_tag::pkt_family::xevent == msg_pkt.kind) { auto const disp = _m_spec().open_display(); @@ -793,35 +822,7 @@ namespace nana { auto pos = API::cursor_position(); auto native_cur_wd = reinterpret_cast(detail::native_interface::find_window(pos.x, pos.y)); -#if 0 - const char* icon = nullptr; - if(hovered_.native_wd != native_cur_wd) - { - if(hovered_.native_wd) - { - _m_free_cursor(); - ::XUndefineCursor(disp, hovered_.native_wd); - } - _m_client_msg(native_cur_wd, native_source, 1, atombase.xdnd_enter, atombase.text_uri_list, XA_STRING); - hovered_.native_wd = native_cur_wd; - - if(!ddrop->simple_mode()) - icon = "dnd-move"; - } - - if(ddrop->simple_mode()) - { - auto cur_wd = API::find_window(API::cursor_position()); - if(hovered_.window_handle != cur_wd) - { - hovered_.window_handle = cur_wd; - - icon = (((drag_wd == cur_wd) || ddrop->has(drag_wd, cur_wd)) ? "dnd-move" : "dnd-none"); - } - } - -#else const char* icon = nullptr; if(hovered_.native_wd != native_cur_wd) { @@ -835,23 +836,19 @@ namespace nana hovered_.native_wd = native_cur_wd; } - if(ddrop->simple_mode()) + + auto cur_wd = API::find_window(API::cursor_position()); + + std::cout<<" Hovered="<data()); + auto data = dropdata->data()->to_xdnd_data(); API::set_capture(drag_wd, true); nana::detail::xdnd_protocol xdnd_proto{native_source}; //Not simple mode - _m_spec().msg_dispatch([this, ddrop, &data, drag_wd, xdnd_proto, native_source, &target_wd, &atombase](const detail::msg_packet_tag& msg_pkt) mutable{ + _m_spec().msg_dispatch([this, ddrop, &data, drag_wd, &xdnd_proto, native_source, &atombase](const detail::msg_packet_tag& msg_pkt) mutable{ if(detail::msg_packet_tag::pkt_family::xevent == msg_pkt.kind) { auto const disp = _m_spec().open_display(); @@ -891,7 +889,7 @@ namespace nana auto pos = API::cursor_position(); auto native_cur_wd = reinterpret_cast(detail::native_interface::find_window(pos.x, pos.y)); - xdnd_proto.mouse_move(native_cur_wd, pos); + xdnd_proto.mouse_move(native_cur_wd, pos, data.requested_action); } else if(ClientMessage == msg_pkt.u.xevent.type) { @@ -911,10 +909,12 @@ namespace nana std::cout<<"ButtonRelease"< dropdata{new dragdrop_service::dropdata_type}; - auto has_dropped = dragdrop_service::instance().dragdrop(arg.window_handle, dropdata.get()); + auto has_dropped = dragdrop_service::instance().dragdrop(arg.window_handle, dropdata.get(), nullptr); real_wd->other.dnd_state = dragdrop_status::not_ready; impl_->dragging = false; @@ -1125,7 +1143,7 @@ namespace nana dragdrop_session * ddrop{nullptr}; std::function predicate; std::function generator; - std::function drop_finished; + std::function drop_finished; struct event_handlers { @@ -1143,28 +1161,18 @@ namespace nana void make_drop() { + if (!generator) + return; + auto transf_data = generator(); dragdrop_service::dropdata_type dropdata; dropdata.assign(*transf_data.real_data_); -/* //deprecated -#ifdef NANA_WINDOWS - drop_source drop_src{ source_handle }; - DWORD result_effect = DROPEFFECT_NONE; - auto status = ::DoDragDrop(&dropdata, &drop_src, DROPEFFECT_COPY | DROPEFFECT_MOVE, &result_effect); - if (DROPEFFECT_NONE == result_effect) - { - } - - if (drop_finished) - drop_finished(DROPEFFECT_NONE != result_effect); -#else -#endif -*/ - auto has_dropped = dragdrop_service::instance().dragdrop(source_handle, &dropdata); + dnd_action executed_action; + auto has_dropped = dragdrop_service::instance().dragdrop(source_handle, &dropdata, &executed_action); if(drop_finished) - drop_finished(has_dropped); + drop_finished(has_dropped, executed_action, transf_data); } }; @@ -1240,15 +1248,16 @@ namespace nana impl_->generator = generator; } - void dragdrop::drop_finished(std::function finish_fn) + void dragdrop::drop_finished(std::function finish_fn) { impl_->drop_finished = finish_fn; } - dragdrop::data::data(): + dragdrop::data::data(dnd_action requested_action): real_data_(new detail::dragdrop_data) { + real_data_->requested_action = requested_action; } dragdrop::data::~data() From bed829fa26a666044eb6d66d333dea91e3e57ca3 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Sat, 29 Dec 2018 06:38:46 +0800 Subject: [PATCH 114/149] fix bug that line alorithm wrongly draws a line when fade_rate is zero --- source/gui/widgets/skeletons/text_editor.cpp | 2 +- source/paint/detail/image_process_provider.cpp | 2 +- .../nana => source}/paint/detail/image_processor.hpp | 12 ++++++++---- 3 files changed, 10 insertions(+), 6 deletions(-) rename {include/nana => source}/paint/detail/image_processor.hpp (98%) diff --git a/source/gui/widgets/skeletons/text_editor.cpp b/source/gui/widgets/skeletons/text_editor.cpp index 72d26a2e..6a5251b4 100644 --- a/source/gui/widgets/skeletons/text_editor.cpp +++ b/source/gui/widgets/skeletons/text_editor.cpp @@ -2002,7 +2002,7 @@ namespace nana { auto fgcolor = scheme_->foreground.get_color(); if (!API::window_enabled(window_)) - fgcolor.blend(bgcolor, 0.5); // do nothing !!! should be replace with fgcolor = fgcolor.blend(bgcolor, 0.5); <\code> or removed + fgcolor = fgcolor.blend(bgcolor, 0.5); //Thank to besh81 for getting the fgcolor to be changed if (API::widget_borderless(window_)) graph_.rectangle(false, bgcolor); diff --git a/source/paint/detail/image_process_provider.cpp b/source/paint/detail/image_process_provider.cpp index 414d68dc..89324e04 100644 --- a/source/paint/detail/image_process_provider.cpp +++ b/source/paint/detail/image_process_provider.cpp @@ -1,7 +1,7 @@ #include #include -#include +#include "image_processor.hpp" namespace nana { diff --git a/include/nana/paint/detail/image_processor.hpp b/source/paint/detail/image_processor.hpp similarity index 98% rename from include/nana/paint/detail/image_processor.hpp rename to source/paint/detail/image_processor.hpp index 1a412a2e..5e7e3548 100644 --- a/include/nana/paint/detail/image_processor.hpp +++ b/source/paint/detail/image_processor.hpp @@ -1,7 +1,7 @@ /* * Image Processor Algorithm 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 @@ -15,8 +15,8 @@ #ifndef NANA_PAINT_DETAIL_IMAGE_PROCESSOR_HPP #define NANA_PAINT_DETAIL_IMAGE_PROCESSOR_HPP -#include "../image_process_interface.hpp" #include +#include #include #include @@ -421,15 +421,19 @@ namespace detail { virtual void process(paint::pixel_buffer & pixbuf, const nana::point& pos_beg, const nana::point& pos_end, const ::nana::color& clr, double fade_rate) const { + //Return if it is completely transparent + if (fade_rate <= 0) + return; + auto rgb_color = clr.px_color().value; const std::size_t bytes_pl = pixbuf.bytes_per_line(); unsigned char * fade_table = nullptr; std::unique_ptr autoptr; nana::pixel_argb_t rgb_imd = {}; - if(fade_rate != 0.0) + if(fade_rate < 1) { - autoptr = detail::alloc_fade_table(1 - fade_rate); + autoptr = detail::alloc_fade_table(1.0 - fade_rate); fade_table = autoptr.get(); rgb_imd.value = rgb_color; rgb_imd = detail::fade_color_intermedia(rgb_imd, fade_table); From 345d65f6c9abe38c43daf90aad4ac63f81cd05a9 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Sat, 29 Dec 2018 07:08:49 +0800 Subject: [PATCH 115/149] add caption background mode for group --- include/nana/gui/widgets/group.hpp | 10 ++++++- source/gui/widgets/group.cpp | 48 ++++++++++++++++++++++++------ 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/include/nana/gui/widgets/group.hpp b/include/nana/gui/widgets/group.hpp index ba48928b..e0fdfc3b 100644 --- a/include/nana/gui/widgets/group.hpp +++ b/include/nana/gui/widgets/group.hpp @@ -42,6 +42,13 @@ namespace nana{ using field_reference = place::field_reference; constexpr static const std::size_t npos = static_cast(-1); + enum class background_mode + { + none, + transparent, + blending + }; + /// The default construction group(); @@ -66,7 +73,8 @@ namespace nana{ checkbox& add_option(::std::string); /// Modifies the alignment of the title - void caption_align(align position); + group& caption_align(align position); + group& caption_background_mode(background_mode mode); /// Enables/disables the radio mode which is single selection group& radio_mode(bool); diff --git a/source/gui/widgets/group.cpp b/source/gui/widgets/group.cpp index 6d697a62..d85d81a9 100644 --- a/source/gui/widgets/group.cpp +++ b/source/gui/widgets/group.cpp @@ -36,6 +36,7 @@ namespace nana{ { label caption; align caption_align{ align::left }; + background_mode caption_mode{ background_mode::blending }; place place_content; unsigned gap{2}; std::string usr_div_str; @@ -154,7 +155,7 @@ namespace nana{ return *impl_->options.back(); } - void group::caption_align(align position) + group& group::caption_align(align position) { if (position != impl_->caption_align) { @@ -163,6 +164,32 @@ namespace nana{ impl_->place_content.collocate(); API::refresh_window(*this); } + return *this; + } + + group& group::caption_background_mode(background_mode mode) + { + if (mode != impl_->caption_mode) + { + impl_->caption_mode = mode; + switch (mode) + { + case background_mode::none: + impl_->caption.bgcolor(this->bgcolor()); + impl_->caption.transparent(false); + break; + case background_mode::blending: + impl_->caption.transparent(true); + impl_->caption.bgcolor(API::bgcolor(this->parent()).blend(colors::black, 0.025)); + break; + case background_mode::transparent: + impl_->caption.transparent(true); + impl_->caption.bgcolor(API::bgcolor(this->parent()).blend(colors::black, 0.025)); + break; + } + API::refresh_window(*this); + } + return *this; } group& group::radio_mode(bool enable) @@ -289,17 +316,20 @@ namespace nana{ ), 3, 3, this->scheme().border, true, this->bgcolor()); - auto opt_r = API::window_rectangle(impl_->caption); - if (opt_r) + if (background_mode::blending == impl_->caption_mode) { - rectangle grad_r{ opt_r->position(), nana::size{ opt_r->width + 4, static_cast(top_round_line - opt_r->y) } }; + auto opt_r = API::window_rectangle(impl_->caption); + if (opt_r) + { + rectangle grad_r{ opt_r->position(), nana::size{ opt_r->width + 4, static_cast(top_round_line - opt_r->y) } }; - grad_r.y += top_round_line*2 / 3; - grad_r.x -= 2; + grad_r.y += top_round_line * 2 / 3; + grad_r.x -= 2; - graph.gradual_rectangle(grad_r, - API::bgcolor(this->parent()), this->bgcolor(), true - ); + graph.gradual_rectangle(grad_r, + API::bgcolor(this->parent()), this->bgcolor(), true + ); + } } }); } From e963c29646085f709782a89f5eab98b64e3517fe Mon Sep 17 00:00:00 2001 From: Jinhao Date: Fri, 4 Jan 2019 21:58:48 +0800 Subject: [PATCH 116/149] fix crash occurs when switch between radio button to checkbox(#375) --- include/nana/gui/widgets/checkbox.hpp | 1 + source/gui/widgets/checkbox.cpp | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/include/nana/gui/widgets/checkbox.hpp b/include/nana/gui/widgets/checkbox.hpp index 57239d20..1a75c91a 100644 --- a/include/nana/gui/widgets/checkbox.hpp +++ b/include/nana/gui/widgets/checkbox.hpp @@ -104,6 +104,7 @@ namespace drawerbase struct element_tag { checkbox * uiobj; + event_handle eh_clicked; event_handle eh_checked; event_handle eh_destroy; event_handle eh_keyboard; diff --git a/source/gui/widgets/checkbox.cpp b/source/gui/widgets/checkbox.cpp index a43cd908..3148ee88 100644 --- a/source/gui/widgets/checkbox.cpp +++ b/source/gui/widgets/checkbox.cpp @@ -216,6 +216,7 @@ namespace nana{ namespace drawerbase { e.uiobj->radio(false); e.uiobj->react(true); + API::umake_event(e.eh_clicked); API::umake_event(e.eh_checked); API::umake_event(e.eh_destroy); API::umake_event(e.eh_keyboard); @@ -232,7 +233,7 @@ namespace nana{ namespace drawerbase el.uiobj = &uiobj; - uiobj.events().checked.connect_unignorable([this](const arg_checkbox& arg) + el.eh_checked = uiobj.events().checked.connect_unignorable([this](const arg_checkbox& arg) { if (arg.widget->checked()) { @@ -244,7 +245,7 @@ namespace nana{ namespace drawerbase } }, true); - el.eh_checked = uiobj.events().click.connect_unignorable([this](const arg_click& arg) + el.eh_clicked = uiobj.events().click.connect_unignorable([this](const arg_click& arg) { for (auto & i : ui_container_) i.uiobj->check(arg.window_handle == i.uiobj->handle()); From 96f62a7b0835b323aa09ca8d20792418cef7d1cf Mon Sep 17 00:00:00 2001 From: Jinhao Date: Sun, 6 Jan 2019 08:29:43 +0800 Subject: [PATCH 117/149] fix bug that text_editor::text use \n\r as end of line --- source/gui/widgets/skeletons/text_editor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/gui/widgets/skeletons/text_editor.cpp b/source/gui/widgets/skeletons/text_editor.cpp index 6a5251b4..f53a5837 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-2018 Jinhao(cnjinhao@hotmail.com) +* Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -1728,7 +1728,7 @@ namespace nana { str = impl_->textbase.getline(0); for (std::size_t i = 1; i < lines; ++i) { - str += L"\n\r"; + str += L"\r\n"; str += impl_->textbase.getline(i); } } From 4c3455648f7ed44f7eb62d3578ee0f83801bcb2b Mon Sep 17 00:00:00 2001 From: besh81 Date: Tue, 8 Jan 2019 17:31:39 +0100 Subject: [PATCH 118/149] spinbox: if not editable keep prefix and suffix when gain focus --- source/gui/widgets/spinbox.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/gui/widgets/spinbox.cpp b/source/gui/widgets/spinbox.cpp index 6ed1f3ed..49ae0347 100644 --- a/source/gui/widgets/spinbox.cpp +++ b/source/gui/widgets/spinbox.cpp @@ -467,7 +467,7 @@ namespace nana std::wstring text; - if (API::is_focus_ready(editor_->window_handle())) + if (API::is_focus_ready(editor_->window_handle()) && editor_->attr().editable) text = to_wstring(range_->value()); else text = to_wstring(modifier_.prefix + range_->value() + modifier_.suffix); From 26c28ae4becac7b7029152993ce3eac940c3d998 Mon Sep 17 00:00:00 2001 From: James Bremner Date: Wed, 16 Jan 2019 16:43:05 -0500 Subject: [PATCH 119/149] Slider value handles -ve values by clipping to zero see http://nanapro.org/en-us/forum/index.php?u=/topic/1092/ggslider-value-range --- include/nana/gui/widgets/slider.hpp | 8 +++++++- source/gui/widgets/slider.cpp | 24 ++++++++++++++---------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/include/nana/gui/widgets/slider.hpp b/include/nana/gui/widgets/slider.hpp index 8ce9602b..2fc015ce 100644 --- a/include/nana/gui/widgets/slider.hpp +++ b/include/nana/gui/widgets/slider.hpp @@ -151,7 +151,13 @@ namespace nana bool vertical() const; void maximum(unsigned); unsigned maximum() const; - void value(unsigned); + + /** Set slider value + @param[in] v new value for slider. + v will be clipped to the range 0 to maximum + */ + void value(int ); + unsigned value() const; unsigned move_step(bool forward); ///< Increase or decrease the value of slider. unsigned adorn() const; diff --git a/source/gui/widgets/slider.cpp b/source/gui/widgets/slider.cpp index 67a67631..572eba99 100644 --- a/source/gui/widgets/slider.cpp +++ b/source/gui/widgets/slider.cpp @@ -245,7 +245,7 @@ namespace nana }; public: enum class parts{none, bar, slider}; - + using graph_reference = drawer_trigger::graph_reference; model() @@ -256,7 +256,7 @@ namespace nana proto_.renderer = pat::cloneable{interior_renderer{}}; attr_.seek_dir = seekdir::bilateral; - + attr_.is_draw_adorn = false; attr_.vcur = 0; attr_.vmax = 10; @@ -361,7 +361,7 @@ namespace nana parts seek_where(::nana::point pos) const { nana::rectangle r = _m_bar_area(); - + if (attr_.slider.vert) { std::swap(pos.x, pos.y); @@ -373,7 +373,7 @@ namespace nana return parts::slider; sdpos = static_cast(attr_.slider.weight) / 2; - + if (sdpos <= pos.x && pos.x < sdpos + static_cast(r.width)) { if(pos.y < r.bottom()) @@ -446,7 +446,7 @@ namespace nana bool move_slider(const ::nana::point& pos) { int adorn_pos = slider_state_.snap_pos + (attr_.slider.vert ? pos.y : pos.x) - slider_state_.refpos.x; - + if (adorn_pos > 0) { int range = static_cast(_m_range()); @@ -691,7 +691,7 @@ namespace nana window wd; nana::slider * widget; }other_; - + struct prototype_tag { pat::cloneable renderer; @@ -759,9 +759,9 @@ namespace nana void trigger::mouse_move(graph_reference graph, const arg_mouse& arg) { // check if slider is disabled - if(!API::get_widget(arg.window_handle)->enabled()) + if(!API::get_widget(arg.window_handle)->enabled()) return; // do nothing - + bool updated = false; if (model_ptr_->if_trace_slider()) { @@ -804,7 +804,7 @@ namespace nana //class slider slider::slider(){} - + slider::slider(window wd, bool visible) { create(wd, rectangle(), visible); @@ -844,10 +844,14 @@ namespace nana return get_drawer_trigger().get_model()->attribute().vmax; } - void slider::value(unsigned v) + void slider::value(int v) { if(handle()) { + // limit to positive values, vcur expects unsigned + if( v < 0 ) + v = 0; + if(get_drawer_trigger().get_model()->vcur(v)) API::refresh_window(handle()); } From f488df3e4c3046de19f351883bf557f64bfa7c35 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Thu, 17 Jan 2019 07:10:16 +0800 Subject: [PATCH 120/149] add support of refreshing a draw_through form --- source/gui/detail/native_window_interface.cpp | 17 +++++++++-------- source/gui/detail/window_manager.cpp | 6 ++++++ source/gui/programming_interface.cpp | 1 - 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/source/gui/detail/native_window_interface.cpp b/source/gui/detail/native_window_interface.cpp index 8d3dd9c8..fcf7f695 100644 --- a/source/gui/detail/native_window_interface.cpp +++ b/source/gui/detail/native_window_interface.cpp @@ -848,12 +848,17 @@ namespace nana{ #endif } - void native_interface::refresh_window(native_window_type wd) + void native_interface::refresh_window(native_window_type native_wd) { #if defined(NANA_WINDOWS) - ::InvalidateRect(reinterpret_cast(wd), nullptr, true); + auto wd = reinterpret_cast(native_wd); + RECT r; + ::GetClientRect(wd, &r); + ::InvalidateRect(wd, &r, FALSE); #elif defined(NANA_X11) - static_cast(wd); //eliminate unused parameter compiler warning. + Display * disp = restrict::spec.open_display(); + ::XClearArea(disp, reinterpret_cast(native_wd), 0, 0, 1, 1, true); + ::XFlush(disp); #endif } @@ -1110,14 +1115,10 @@ namespace nana{ if(owner && (owner != reinterpret_cast(restrict::spec.root_window()))) { auto origin = window_position(owner); -#if 0 - x += origin.x; - y += origin.y; -#else + auto owner_extents = window_frame_extents(owner); x += origin.x + owner_extents.left; y += origin.y + owner_extents.top; -#endif } ::XMoveResizeWindow(disp, reinterpret_cast(wd), x, y, r.width, r.height); diff --git a/source/gui/detail/window_manager.cpp b/source/gui/detail/window_manager.cpp index 9d8ebd65..c9184053 100644 --- a/source/gui/detail/window_manager.cpp +++ b/source/gui/detail/window_manager.cpp @@ -1091,6 +1091,12 @@ namespace detail std::lock_guard lock(mutex_); if (impl_->wd_register.available(wd) == false) return false; + if ((wd->other.category == category::flags::root) && wd->is_draw_through()) + { + native_interface::refresh_window(wd->root); + return true; + } + if (wd->displayed()) { using paint_operation = window_layer::paint_operation; diff --git a/source/gui/programming_interface.cpp b/source/gui/programming_interface.cpp index 1f3ac8c9..9791d2fd 100644 --- a/source/gui/programming_interface.cpp +++ b/source/gui/programming_interface.cpp @@ -962,7 +962,6 @@ namespace API restrict::wd_manager().update(reinterpret_cast(wd), false, true); } - void window_caption(window wd, const std::string& title_utf8) { throw_not_utf8(title_utf8); From 9a523366459d1dfc022ccf341c3e7858d79c48af Mon Sep 17 00:00:00 2001 From: Ariel Vina-Rodriguez Date: Fri, 18 Jan 2019 11:09:16 +0100 Subject: [PATCH 121/149] typos --- source/gui/widgets/listbox.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/gui/widgets/listbox.cpp b/source/gui/widgets/listbox.cpp index dd970a65..dcdd0eca 100644 --- a/source/gui/widgets/listbox.cpp +++ b/source/gui/widgets/listbox.cpp @@ -132,7 +132,7 @@ namespace nana { native_string_type caption; //< header title unsigned width_px; //< column width in pixels - std::pair range_width_px; //< allowed witdh + std::pair range_width_px; //< allowed width bool visible_state{ true }; @@ -441,7 +441,7 @@ namespace nana throw std::invalid_argument("listbox: invalid header index"); } - /// find and return a ref to the column that originaly was at position "pos" previous to any list reorganization. + /// find and return a ref to the column that originally was at position "pos" previous to any list reorganization. column& at(size_type pos, bool disp_order = false) { check_range(pos, cont_.size()); @@ -508,7 +508,7 @@ namespace nana return{ left, 0 }; } - /// return the original index of the visible col currently before(in front of) or after the col originaly at index "index" + /// return the original index of the visible col currently before(in front of) or after the col originally at index "index" size_type next(size_type index) const noexcept { bool found_me = false; @@ -561,7 +561,7 @@ namespace nana cont_.insert(cont_.begin()+ view, col_from); } - /// move the col originaly at "from" to the position currently in front (or after) the col originaly at index "to" invalidating some current index + /// move the col originally at "from" to the position currently in front (or after) the col originally at index "to" invalidating some current index void move(size_type from, size_type to, bool front) noexcept { if ((from == to) || (from >= cont_.size()) || (to >= cont_.size())) From 5935ce20360193fae16a2729472762d1f4fa6c9b Mon Sep 17 00:00:00 2001 From: Jinhao Date: Sat, 19 Jan 2019 03:09:52 +0800 Subject: [PATCH 122/149] fix bug that listbox item automatically deselected item automatically are deselected when release mouse after mouse selection(develop-1.7) --- source/gui/widgets/listbox.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/source/gui/widgets/listbox.cpp b/source/gui/widgets/listbox.cpp index 4f93a660..0c797c57 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-2018 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -4110,11 +4110,13 @@ namespace nana essence_->draw_peripheral(); } + // In mouse move event, it cancels the msup_deselect if the listbox is draggable or it started the mouse selection. void trigger::mouse_move(graph_reference graph, const arg_mouse& arg) { using item_state = essence::item_state; using parts = essence::parts; + //Don't deselect the items if the listbox is draggable if ((operation_states::msup_deselect == essence_->operation.state) && API::dev::window_draggable(arg.window_handle)) essence_->operation.state = operation_states::none; @@ -4168,6 +4170,9 @@ namespace nana if (essence_->mouse_selection.started) { essence_->update_mouse_selection(arg.pos); + + //Don't deselect items if the mouse selection is started + essence_->operation.state = operation_states::none; need_refresh = true; } @@ -4411,7 +4416,7 @@ namespace nana essence_->stop_mouse_selection(); need_refresh = true; } - + if (operation_states::msup_deselect == essence_->operation.state) { essence_->operation.state = operation_states::none; From a71e02b5f2025a9df3851964a5414ae658bcd277 Mon Sep 17 00:00:00 2001 From: besh81 Date: Sat, 19 Jan 2019 15:04:19 +0100 Subject: [PATCH 123/149] adding treebox scheme --- include/nana/gui/widgets/treebox.hpp | 19 ++++++++- source/gui/widgets/treebox.cpp | 60 +++++++++++++++------------- 2 files changed, 50 insertions(+), 29 deletions(-) diff --git a/include/nana/gui/widgets/treebox.hpp b/include/nana/gui/widgets/treebox.hpp index ef804ff9..bf8c2746 100644 --- a/include/nana/gui/widgets/treebox.hpp +++ b/include/nana/gui/widgets/treebox.hpp @@ -347,13 +347,30 @@ namespace nana basic_event selected; ///< a user selects or unselects a node basic_event hovered; ///< a user moves the cursor over a node }; + + + struct scheme + : public widget_geometrics + { + color_proxy item_bg_selected{ static_cast(0xD5EFFC) }; ///< item selected: background color + color_proxy item_fg_selected{ static_cast(0x99DEFD) }; ///< item selected: foreground color + color_proxy item_bg_highlighted{ static_cast(0xE8F5FD) }; ///< item highlighted: background color + color_proxy item_fg_highlighted{ static_cast(0xD8F0FA) }; ///< item highlighted: foreground color + color_proxy item_bg_selected_and_highlighted{ static_cast(0xC4E8FA) }; ///< item selected and highlighted: background color + color_proxy item_fg_selected_and_highlighted{ static_cast(0xB6E6FB) }; ///< item selected and highlighted: foreground color + + + unsigned item_offset{ 16 }; + unsigned text_offset{ 4 }; + unsigned indent_displacement{ 18 }; ///< children position displacement in pixels (def=18 (before was 10)) + }; }//end namespace treebox }//end namespace drawerbase /// \brief Displays a hierarchical list of items, such as the files and directories on a disk. /// See also in [documentation](http://nanapro.org/en-us/documentation/widgets/treebox.htm) class treebox - :public widget_object < category::widget_tag, drawerbase::treebox::trigger, drawerbase::treebox::treebox_events> + :public widget_object { public: /// A type refers to the item and is also used to iterate through the nodes. diff --git a/source/gui/widgets/treebox.cpp b/source/gui/widgets/treebox.cpp index cf5c4ea8..8152f42d 100644 --- a/source/gui/widgets/treebox.cpp +++ b/source/gui/widgets/treebox.cpp @@ -238,11 +238,11 @@ namespace nana switch (affect) { case 1: - pos_.x += impl_->shape.indent_pixels; + pos_.x += impl_->data.scheme_ptr->indent_displacement; break; default: if (affect >= 2) - pos_.x -= impl_->shape.indent_pixels * (affect - 1); + pos_.x -= impl_->data.scheme_ptr->indent_displacement * (affect - 1); } auto & comp_placer = impl_->data.comp_placer; @@ -301,6 +301,7 @@ namespace nana { nana::paint::graphics * graph; ::nana::treebox * widget_ptr; + ::nana::treebox::scheme_type* scheme_ptr; trigger * trigger_ptr; pat::cloneable comp_placer; @@ -310,13 +311,11 @@ namespace nana struct shape_tag { - nana::upoint border; std::shared_ptr> scroll; mutable std::map image_table; tree_cont_type::node_type * first; //The node at the top of screen - int indent_pixels; int offset_x; }shape; @@ -356,7 +355,6 @@ namespace nana data.stop_drawing = false; shape.first = nullptr; - shape.indent_pixels = 10; shape.offset_x = 0; shape.scroll = std::make_shared>(); @@ -451,7 +449,7 @@ namespace nana data.graph->rectangle(true, data.widget_ptr->bgcolor()); //Draw tree - attr.tree_cont.for_each(shape.first, item_rendering_director(this, nana::point(static_cast(attr.tree_cont.indent_size(shape.first) * shape.indent_pixels) - shape.offset_x, margin_top_bottom()))); + attr.tree_cont.for_each(shape.first, item_rendering_director(this, nana::point(static_cast(attr.tree_cont.indent_size(shape.first) * data.scheme_ptr->indent_displacement) - shape.offset_x, margin_top_bottom()))); if (!ignore_update) API::update_window(data.widget_ptr->handle()); @@ -711,7 +709,7 @@ namespace nana case 3: //param is the begin pos of an item in absolute. { - int beg = static_cast(tree.indent_size(node) * shape.indent_pixels) - shape.offset_x; + int beg = static_cast(tree.indent_size(node) * data.scheme_ptr->indent_displacement) - shape.offset_x; int end = beg + static_cast(node_w_pixels(node)); bool take_adjust = false; @@ -910,7 +908,7 @@ namespace nana bool track_mouse(int x, int y) { - int xpos = attr.tree_cont.indent_size(shape.first) * shape.indent_pixels - shape.offset_x; + int xpos = attr.tree_cont.indent_size(shape.first) * data.scheme_ptr->indent_displacement - shape.offset_x; item_locator nl(this, xpos, x, y); attr.tree_cont.template for_each(shape.first, nl); @@ -1449,37 +1447,42 @@ namespace nana if(compset->comp_attribute(component::bground, attr)) { - const ::nana::color color_table[][2] = { { { 0xE8, 0xF5, 0xFD }, { 0xD8, 0xF0, 0xFA } }, //highlighted - { { 0xC4, 0xE8, 0xFA }, { 0xB6, 0xE6, 0xFB } }, //Selected and highlighted - { { 0xD5, 0xEF, 0xFC }, {0x99, 0xDE, 0xFD } } //Selected but not highlighted - }; + auto scheme_ptr = static_cast<::nana::treebox::scheme_type*>(API::dev::get_scheme(window_handle_)); - const ::nana::color *clrptr = nullptr; + const ::nana::color_proxy *bg_ptr = nullptr, *fg_ptr = nullptr; if(compset->item_attribute().mouse_pointed) { if(compset->item_attribute().selected) - clrptr = color_table[1]; + { + bg_ptr = &scheme_ptr->item_bg_selected_and_highlighted; + fg_ptr = &scheme_ptr->item_fg_selected_and_highlighted; + } else - clrptr = color_table[0]; + { + bg_ptr = &scheme_ptr->item_bg_highlighted; + fg_ptr = &scheme_ptr->item_fg_highlighted; + } } else if(compset->item_attribute().selected) - clrptr = color_table[2]; + { + bg_ptr = &scheme_ptr->item_bg_selected; + fg_ptr = &scheme_ptr->item_fg_selected; + } - if (clrptr) + if(bg_ptr) { if (API::is_transparent_background(window_handle_)) { paint::graphics item_graph{ attr.area.dimension() }; - item_graph.rectangle(false, clrptr[1]); - item_graph.rectangle(rectangle{attr.area.dimension()}.pare_off(1), true, *clrptr); - + item_graph.rectangle(false, *fg_ptr); + item_graph.rectangle(rectangle{ attr.area.dimension() }.pare_off(1), true, *bg_ptr); graph.blend(attr.area, item_graph, attr.area.position(), 0.5); } else { - graph.rectangle(attr.area, false, clrptr[1]); - graph.rectangle(attr.area.pare_off(1), true, *clrptr); + graph.rectangle(attr.area, false, *fg_ptr); + graph.rectangle(attr.area.pare_off(1), true, *bg_ptr); } } } @@ -1577,10 +1580,10 @@ namespace nana switch(affect) { case 0: break; - case 1: item_pos_.x += static_cast(node_desc.indent_pixels); break; + case 1: item_pos_.x += static_cast(impl_->data.scheme_ptr->indent_displacement); break; default: if(affect >= 2) - item_pos_.x -= static_cast(node_desc.indent_pixels) * (affect - 1); + item_pos_.x -= static_cast(impl_->data.scheme_ptr->indent_displacement) * (affect - 1); } impl_->assign_node_attr(node_attr_, &node); @@ -1942,7 +1945,8 @@ namespace nana impl_->data.graph = &graph; widget.bgcolor(colors::white); - impl_->data.widget_ptr = static_cast< ::nana::treebox*>(&widget); + impl_->data.widget_ptr = static_cast<::nana::treebox*>(&widget); + impl_->data.scheme_ptr = static_cast<::nana::treebox::scheme_type*>(API::dev::get_scheme(widget)); widget.caption("nana treebox"); } @@ -1961,7 +1965,7 @@ namespace nana { auto & shape = impl_->shape; - int xpos = impl_->attr.tree_cont.indent_size(shape.first) * shape.indent_pixels - shape.offset_x; + int xpos = impl_->attr.tree_cont.indent_size(shape.first) * impl_->data.scheme_ptr->indent_displacement - shape.offset_x; item_locator nl(impl_, xpos, arg.pos.x, arg.pos.y); impl_->attr.tree_cont.for_each(shape.first, nl); @@ -1986,7 +1990,7 @@ namespace nana { auto & shape = impl_->shape; - int xpos = impl_->attr.tree_cont.indent_size(shape.first) * shape.indent_pixels - shape.offset_x; + int xpos = impl_->attr.tree_cont.indent_size(shape.first) * impl_->data.scheme_ptr->indent_displacement - shape.offset_x; item_locator nl(impl_, xpos, arg.pos.x, arg.pos.y); impl_->attr.tree_cont.for_each(shape.first, nl); @@ -2013,7 +2017,7 @@ namespace nana { auto & shape = impl_->shape; - int xpos = impl_->attr.tree_cont.indent_size(shape.first) * shape.indent_pixels - shape.offset_x; + int xpos = impl_->attr.tree_cont.indent_size(shape.first) * impl_->data.scheme_ptr->indent_displacement - shape.offset_x; item_locator nl(impl_, xpos, arg.pos.x, arg.pos.y); impl_->attr.tree_cont.for_each(shape.first, nl); From 91dfaa5d7ecb0e63f052349f438343038cc1706d Mon Sep 17 00:00:00 2001 From: besh81 Date: Mon, 21 Jan 2019 11:37:35 +0100 Subject: [PATCH 124/149] added treebox scheme --- include/nana/gui/widgets/detail/compset.hpp | 16 +++++++- include/nana/gui/widgets/treebox.hpp | 37 +++++++++--------- source/gui/widgets/treebox.cpp | 43 +++++++++++---------- 3 files changed, 55 insertions(+), 41 deletions(-) diff --git a/include/nana/gui/widgets/detail/compset.hpp b/include/nana/gui/widgets/detail/compset.hpp index 7e77629d..3991fe4e 100644 --- a/include/nana/gui/widgets/detail/compset.hpp +++ b/include/nana/gui/widgets/detail/compset.hpp @@ -46,7 +46,7 @@ namespace nana{ namespace widgets{ namespace detail }; /// A component set placer used for specifying component position and size. - template + template class compset_placer { public: @@ -56,10 +56,22 @@ namespace nana{ namespace widgets{ namespace detail /// A type of widget-defined item attribute. typedef ItemAttribute item_attribute_t; + + /// Widget scheme. + typedef WidgetScheme widget_scheme_t; + + widget_scheme_t * wdg_scheme_ptr_{ nullptr }; + public: - /// The destrcutor. + /// The destructor. virtual ~compset_placer(){} + /// Init the scheme pointer + void init_scheme(widget_scheme_t* wdg_scheme_ptr) + { + wdg_scheme_ptr_ = wdg_scheme_ptr; + } + /// Enable/Disable the specified component. virtual void enable(component_t, bool) = 0; virtual bool enabled(component_t) const = 0; diff --git a/include/nana/gui/widgets/treebox.hpp b/include/nana/gui/widgets/treebox.hpp index bf8c2746..4ff78e99 100644 --- a/include/nana/gui/widgets/treebox.hpp +++ b/include/nana/gui/widgets/treebox.hpp @@ -60,8 +60,26 @@ namespace nana ::std::string text; }; + struct scheme + : public widget_geometrics + { + color_proxy item_bg_selected{ static_cast(0xD5EFFC) }; ///< item selected: background color + color_proxy item_fg_selected{ static_cast(0x99DEFD) }; ///< item selected: foreground color + color_proxy item_bg_highlighted{ static_cast(0xE8F5FD) }; ///< item highlighted: background color + color_proxy item_fg_highlighted{ static_cast(0xD8F0FA) }; ///< item highlighted: foreground color + color_proxy item_bg_selected_and_highlighted{ static_cast(0xC4E8FA) }; ///< item selected and highlighted: background color + color_proxy item_fg_selected_and_highlighted{ static_cast(0xB6E6FB) }; ///< item selected and highlighted: foreground color + + unsigned item_offset{ 16 }; ///< item position displacement in pixels + unsigned text_offset{ 4 }; ///< text position displacement in pixels + unsigned icon_size{ 16 }; ///< icon size in pixels + unsigned crook_size{ 16 }; ///< crook size in pixels (TODO: the function that draw the crook doesn't scale the shape) + + unsigned indent_displacement{ 18 }; ///< children position displacement in pixels (def=18 (before was 10)) + }; + typedef widgets::detail::compset compset_interface; - typedef widgets::detail::compset_placer compset_placer_interface; + typedef widgets::detail::compset_placer compset_placer_interface; class renderer_interface { @@ -347,23 +365,6 @@ namespace nana basic_event selected; ///< a user selects or unselects a node basic_event hovered; ///< a user moves the cursor over a node }; - - - struct scheme - : public widget_geometrics - { - color_proxy item_bg_selected{ static_cast(0xD5EFFC) }; ///< item selected: background color - color_proxy item_fg_selected{ static_cast(0x99DEFD) }; ///< item selected: foreground color - color_proxy item_bg_highlighted{ static_cast(0xE8F5FD) }; ///< item highlighted: background color - color_proxy item_fg_highlighted{ static_cast(0xD8F0FA) }; ///< item highlighted: foreground color - color_proxy item_bg_selected_and_highlighted{ static_cast(0xC4E8FA) }; ///< item selected and highlighted: background color - color_proxy item_fg_selected_and_highlighted{ static_cast(0xB6E6FB) }; ///< item selected and highlighted: foreground color - - - unsigned item_offset{ 16 }; - unsigned text_offset{ 4 }; - unsigned indent_displacement{ 18 }; ///< children position displacement in pixels (def=18 (before was 10)) - }; }//end namespace treebox }//end namespace drawerbase diff --git a/source/gui/widgets/treebox.cpp b/source/gui/widgets/treebox.cpp index 8152f42d..9deb3bd2 100644 --- a/source/gui/widgets/treebox.cpp +++ b/source/gui/widgets/treebox.cpp @@ -1330,8 +1330,6 @@ namespace nana class internal_placer : public compset_placer_interface { - static const unsigned item_offset = 16; - static const unsigned text_offset = 4; private: //Implement the compset_locator_interface @@ -1340,10 +1338,10 @@ namespace nana switch(comp) { case component_t::crook: - pixels_crook_ = (enabled ? 16 : 0); + enable_crook_ = enabled; break; case component_t::icon: - pixels_icon_ = (enabled ? 16 : 0); + enable_icon_ = enabled; break; default: break; @@ -1355,9 +1353,9 @@ namespace nana switch(comp) { case component_t::crook: - return (0 != pixels_crook_); + return enable_crook_; case component_t::icon: - return (0 != pixels_icon_); + return enable_icon_; default: break; } @@ -1366,16 +1364,17 @@ namespace nana virtual unsigned item_height(graph_reference graph) const override { + auto m = std::max((enable_crook_ ? wdg_scheme_ptr_->crook_size : 0), (enable_icon_ ? wdg_scheme_ptr_->icon_size : 0)); #ifdef _nana_std_has_string_view - return graph.text_extent_size(std::wstring_view{ L"jH{", 3 }).height + 8; + return std::max(m, graph.text_extent_size(std::wstring_view{ L"jH{", 3 }).height + 8); #else - return graph.text_extent_size(L"jH{", 3).height + 8; + return std::max(m, graph.text_extent_size(L"jH{", 3).height + 8); #endif } virtual unsigned item_width(graph_reference graph, const item_attribute_t& attr) const override { - return graph.text_extent_size(attr.text).width + pixels_crook_ + pixels_icon_ + (text_offset << 1) + item_offset; + return graph.text_extent_size(attr.text).width + (enable_crook_ ? wdg_scheme_ptr_->crook_size : 0) + (enable_icon_ ? wdg_scheme_ptr_ ->icon_size: 0) + (wdg_scheme_ptr_->text_offset << 1) + wdg_scheme_ptr_->item_offset; } // Locate a component through the specified coordinate. @@ -1390,35 +1389,35 @@ namespace nana case component_t::expander: if(attr.has_children) { - r->width = item_offset; + r->width = wdg_scheme_ptr_->item_offset; return true; } return false; case component_t::bground: return true; case component_t::crook: - if(pixels_crook_) + if(enable_crook_) { - r->x += item_offset; - r->width = pixels_crook_; + r->x += wdg_scheme_ptr_->item_offset; + r->width = wdg_scheme_ptr_->crook_size; return true; } return false; case component_t::icon: - if(pixels_icon_) + if(enable_icon_) { - r->x += item_offset + pixels_crook_; + r->x += wdg_scheme_ptr_->item_offset + (enable_crook_ ? wdg_scheme_ptr_->crook_size : 0); r->y = 2; - r->width = pixels_icon_; + r->width = wdg_scheme_ptr_->icon_size; r->height -= 2; return true; } return false; case component_t::text: { - auto text_pos = item_offset + pixels_crook_ + pixels_icon_ + text_offset; + auto text_pos = wdg_scheme_ptr_->item_offset + (enable_crook_ ? wdg_scheme_ptr_->crook_size : 0) + (enable_icon_ ? wdg_scheme_ptr_->icon_size : 0) + wdg_scheme_ptr_->text_offset; r->x += text_pos; - r->width -= (text_pos + text_offset); + r->width -= (text_pos + wdg_scheme_ptr_->text_offset); }; return true; default: @@ -1427,8 +1426,8 @@ namespace nana return false; } private: - unsigned pixels_crook_{0}; - unsigned pixels_icon_{0}; + bool enable_crook_{ false }; + bool enable_icon_{ false }; }; class internal_renderer @@ -1534,7 +1533,7 @@ namespace nana if((nullptr == img) || img->empty()) img = &(item_attr.icon_normal); - if(! img->empty()) + if(!img->empty()) { auto size = img->size(); if(size.width > attr.area.width || size.height > attr.area.height) @@ -1947,6 +1946,8 @@ namespace nana widget.bgcolor(colors::white); impl_->data.widget_ptr = static_cast<::nana::treebox*>(&widget); impl_->data.scheme_ptr = static_cast<::nana::treebox::scheme_type*>(API::dev::get_scheme(widget)); + impl_->data.comp_placer->init_scheme(impl_->data.scheme_ptr); + widget.caption("nana treebox"); } From 8ecae7402ccfea7b8f951213bb680a9e9d7fbfd7 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Mon, 21 Jan 2019 22:35:44 +0800 Subject: [PATCH 125/149] fix bug that window has wrong position if it has an ancestor --- source/gui/detail/native_window_interface.cpp | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/source/gui/detail/native_window_interface.cpp b/source/gui/detail/native_window_interface.cpp index fcf7f695..cf01e33d 100644 --- a/source/gui/detail/native_window_interface.cpp +++ b/source/gui/detail/native_window_interface.cpp @@ -1,7 +1,7 @@ /* * Platform Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -29,6 +29,7 @@ #include "../../paint/image_accessor.hpp" + namespace nana{ namespace detail{ @@ -199,7 +200,7 @@ namespace nana{ namespace x11_wait { static Bool configure(Display *disp, XEvent *evt, char *arg) - { + { return disp && evt && arg && (evt->type == ConfigureNotify) && (evt->xconfigure.window == *reinterpret_cast(arg)); } @@ -1019,15 +1020,11 @@ namespace nana{ auto const owner = restrict::spec.get_owner(wd); if(owner && (owner != reinterpret_cast(restrict::spec.root_window()))) { - auto origin = window_position(owner); -#if 0 - x += origin.x; - y += origin.y; -#else - auto owner_extents = window_frame_extents(owner); - x += origin.x + owner_extents.left; - y += origin.y + owner_extents.top; -#endif + int origin_x, origin_y; + Window child_useless_for_API; + ::XTranslateCoordinates(disp, reinterpret_cast(owner), restrict::spec.root_window(), 0, 0, &origin_x, &origin_y, &child_useless_for_API); + x += origin_x; + y += origin_y; } ::XMoveWindow(disp, reinterpret_cast(wd), x, y); @@ -1074,7 +1071,6 @@ namespace nana{ XSizeHints hints; nana::detail::platform_scope_guard psg; - //Returns if the requested rectangle is same with the current rectangle. //In some X-Server versions/implementations, XMapWindow() doesn't generate //a ConfigureNotify if the requested rectangle is same with the current rectangle. @@ -1114,11 +1110,11 @@ namespace nana{ auto const owner = restrict::spec.get_owner(wd); if(owner && (owner != reinterpret_cast(restrict::spec.root_window()))) { - auto origin = window_position(owner); - - auto owner_extents = window_frame_extents(owner); - x += origin.x + owner_extents.left; - y += origin.y + owner_extents.top; + int origin_x, origin_y; + Window child_useless_for_API; + ::XTranslateCoordinates(disp, reinterpret_cast(owner), restrict::spec.root_window(), 0, 0, &origin_x, &origin_y, &child_useless_for_API); + x += origin_x; + y += origin_y; } ::XMoveResizeWindow(disp, reinterpret_cast(wd), x, y, r.width, r.height); From e057724f987c974fae4ebacf36385aaaf87fec77 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Mon, 21 Jan 2019 23:51:45 +0800 Subject: [PATCH 126/149] new method is_single_enabled for listbox --- include/nana/gui/widgets/listbox.hpp | 3 ++- source/gui/widgets/listbox.cpp | 12 ++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/include/nana/gui/widgets/listbox.hpp b/include/nana/gui/widgets/listbox.hpp index d5bb0607..86184718 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-2018 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -1538,6 +1538,7 @@ the nana::detail::basic_window member pointer scheme void enable_single(bool for_selection, bool category_limited); void disable_single(bool for_selection); + bool is_single_enabled(bool for_selection) const noexcept; ///< Determines whether the single selection/check is enabled. export_options& def_export_options(); diff --git a/source/gui/widgets/listbox.cpp b/source/gui/widgets/listbox.cpp index 0c797c57..d7789748 100644 --- a/source/gui/widgets/listbox.cpp +++ b/source/gui/widgets/listbox.cpp @@ -1850,6 +1850,11 @@ namespace nana (for_selection ? single_selection_ : single_check_) = false; } + bool is_single_enabled(bool for_selection) const noexcept + { + return (for_selection ? single_selection_ : single_check_); + } + size_type size_item(size_type cat) const { return get(cat)->items.size(); @@ -5959,9 +5964,16 @@ namespace nana void listbox::disable_single(bool for_selection) { + internal_scope_guard lock; _m_ess().lister.disable_single(for_selection); } + bool listbox::is_single_enabled(bool for_selection) const noexcept + { + internal_scope_guard lock; + return _m_ess().lister.is_single_enabled(for_selection); + } + listbox::export_options& listbox::def_export_options() { return _m_ess().def_exp_options; From 3e9b08c0cc120ef7e0e2c5533cd8c3c5db290fe3 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Sun, 27 Jan 2019 06:14:21 +0800 Subject: [PATCH 127/149] fix issue that reverse box selection works incorrectly new bug from develop-1.7 --- source/gui/widgets/listbox.cpp | 90 ++++++++++++++++++++++++---------- 1 file changed, 64 insertions(+), 26 deletions(-) diff --git a/source/gui/widgets/listbox.cpp b/source/gui/widgets/listbox.cpp index d7789748..f9e5bd0c 100644 --- a/source/gui/widgets/listbox.cpp +++ b/source/gui/widgets/listbox.cpp @@ -2011,6 +2011,8 @@ namespace nana { bool started{ false }; bool reverse_selection{ false }; + bool scroll_direction; + bool deselect_when_start_to_move; point screen_pos; point begin_position; ///< Logical position to the @@ -2018,9 +2020,18 @@ namespace nana index_pairs already_selected; index_pairs selections; - bool scroll_direction; unsigned scroll_step{ 1 }; unsigned mouse_move_timestamp{ 0 }; + + bool is_already_selected(const index_pair& abs_pos) const noexcept + { + return (already_selected.cend() != std::find(already_selected.cbegin(), already_selected.cend(), abs_pos)); + } + + bool is_selected(const index_pair& abs_pos) const noexcept + { + return (selections.cend() != std::find(selections.cbegin(), selections.cend(), abs_pos)); + } }mouse_selection; @@ -2118,6 +2129,7 @@ namespace nana mouse_selection.started = true; mouse_selection.begin_position = logic_pos; mouse_selection.end_position = logic_pos; + mouse_selection.deselect_when_start_to_move = true; if (arg.ctrl || arg.shift) { @@ -2133,6 +2145,17 @@ namespace nana if (!mouse_selection.started) return; + // When the button is pressed and start to move the mouse, the listbox should deselect all items. + // But when ctrl is clicked + if (mouse_selection.deselect_when_start_to_move) + { + mouse_selection.deselect_when_start_to_move = false; + if (mouse_selection.already_selected.empty()) + lister.select_for_all(false); + + mouse_selection.selections.clear(); + } + mouse_selection.screen_pos = screen_pos; auto logic_pos = coordinate_cast(screen_pos, true); @@ -2148,7 +2171,7 @@ namespace nana mouse_selection.end_position = logic_pos; - bool cancel_selections = true; + std::vector> selections; 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 && @@ -2159,18 +2182,18 @@ namespace nana auto begin = lister.advance(lister.first(), begin_off); if (!begin.empty()) { - std::vector> selections; - if ((mouse_selection.end_position.y < 0) || (lister.distance(lister.first(), begin) == begin_off)) { //The range [begin_off, last_off] is a range of box selection auto last_off = (std::max)(mouse_selection.begin_position.y, mouse_selection.end_position.y) / item_height(); auto last = lister.advance(lister.first(), last_off); + //Tries to select the items in the box, then returns the items with their previous selected states selections = lister.select_display_range_if(begin, last, false, [this](const index_pair& abs_pos) { - if (this->mouse_selection.reverse_selection) + if (mouse_selection.reverse_selection) { - if (mouse_selection.already_selected.cend() != std::find(mouse_selection.already_selected.cbegin(), mouse_selection.already_selected.cend(), abs_pos)) + //Deselects the items in the box which has been already selected + if(mouse_selection.is_already_selected(abs_pos)) { item_proxy{ this, abs_pos }.select(false); return false; @@ -2181,21 +2204,21 @@ namespace nana for (auto & pair : selections) { + //Continue if the previous state is selected. It indicates the item now is not selected. if (pair.second) continue; - if (mouse_selection.selections.cend() == - std::find(mouse_selection.selections.cbegin(), mouse_selection.selections.cend(), pair.first)) - { + //Add the item to selections container. + if(!mouse_selection.is_selected(pair.first)) mouse_selection.selections.push_back(pair.first); - } } + //Deselects the items which are in mouse_selection.selections but not in selections. + //Eq to mouse_selection.selections = selections #ifdef _MSC_VER for (auto i = mouse_selection.selections.cbegin(); i != mouse_selection.selections.cend();) #else for(auto i = mouse_selection.selections.begin(); i != mouse_selection.selections.end();) - #endif { auto & selpos = *i; @@ -2209,30 +2232,45 @@ namespace nana else ++i; } - - cancel_selections = false; } } } - if (cancel_selections) + //Restores an already selected item if it is not in selections. + for (auto& abs_pos : mouse_selection.already_selected) { - if (!mouse_selection.already_selected.empty()) + if (selections.cend() == std::find_if(selections.cbegin(), selections.cend(), [abs_pos](const std::pair& rhs) { + return (abs_pos == rhs.first); + })) { - for (auto & pos : mouse_selection.selections) - item_proxy(this, pos).select(false); - - //Don't restore the already selections if it is reverse selection(pressing shift). Behaves like Windows Explorer. - if (!mouse_selection.reverse_selection) + item_proxy m{ this, abs_pos }; + if (!m.selected()) { - for (auto & abs_pos : mouse_selection.already_selected) - item_proxy(this, abs_pos).select(true); + m.select(true); + //Add the item to selections container. + if(!mouse_selection.is_selected(abs_pos)) + mouse_selection.selections.push_back(abs_pos); } } - else - lister.select_for_all(false); + } - mouse_selection.selections.clear(); + //Deselects the item which is not in already_selected and selections but in mouse_selection.selections + for(auto i = mouse_selection.selections.cbegin(); i != mouse_selection.selections.cend();) + { + auto abs_pos = *i; + + bool is_box_selected = (selections.cend() != std::find_if(selections.cbegin(), selections.cend(), [abs_pos](const std::pair& rhs) { + return (abs_pos == rhs.first); + })); + + if (is_box_selected || mouse_selection.is_already_selected(abs_pos)) + { + ++i; + continue; + } + + item_proxy{ this, abs_pos }.select(false); + i = mouse_selection.selections.erase(i); } } @@ -4374,7 +4412,7 @@ namespace nana if (arg.is_left_button() && (!lister.single_status(true))) essence_->start_mouse_selection(arg); - //Deselection of all items is deferred to the mouse up event when ctrl or shift is not pressed + //Deselecting all items is deferred to the mouse up event when ctrl or shift is not pressed //Pressing ctrl or shift is to selects other items without deselecting current selections. if (!(arg.ctrl || arg.shift)) { From b0201a5c238d46c1b654e422a0dffd550e3b1e14 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Sun, 27 Jan 2019 06:19:18 +0800 Subject: [PATCH 128/149] Multiple file selection with filebox on Linux based on beru's PR(#349) --- include/nana/gui/filebox.hpp | 7 +- source/gui/filebox.cpp | 563 ++++++++++++++++++++++++++++++----- 2 files changed, 498 insertions(+), 72 deletions(-) diff --git a/include/nana/gui/filebox.hpp b/include/nana/gui/filebox.hpp index bce6af78..61ebdfa2 100644 --- a/include/nana/gui/filebox.hpp +++ b/include/nana/gui/filebox.hpp @@ -1,7 +1,7 @@ /** * Filebox * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -69,11 +69,10 @@ namespace nana const ::std::string& path() const; - const ::std::string& file() const; -#if defined(NANA_WINDOWS) + ::std::string file() const; + const ::std::vector<::std::string>& files() const; void allow_multi_select(bool allow); -#endif /// Display the filebox dialog bool show() const; diff --git a/source/gui/filebox.cpp b/source/gui/filebox.cpp index 43bbf2dc..a8848e1e 100644 --- a/source/gui/filebox.cpp +++ b/source/gui/filebox.cpp @@ -1,7 +1,7 @@ /* * Filebox * Nana C++ Library(http://www.nanapro.org) -* Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) +* Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -38,6 +38,8 @@ # include "../detail/posix/theme.hpp" #endif +#include //debug + namespace fs = std::filesystem; namespace fs_ext = nana::filesystem_ext; @@ -153,7 +155,7 @@ namespace nana typedef treebox::item_proxy item_proxy; public: - filebox_implement(window owner, mode dialog_mode, const std::string& title, bool pick_directory = false): + filebox_implement(window owner, mode dialog_mode, const std::string& title, bool pick_directory, bool allow_multi_select): form(owner, API::make_center(owner, 630, 440)), pick_directory_(pick_directory), mode_(dialog_mode) @@ -239,11 +241,42 @@ namespace nana ls_file_.append_header(i18n("NANA_FILEBOX_HEADER_TYPE"), 80); ls_file_.append_header(i18n("NANA_FILEBOX_HEADER_SIZE"), 70); - auto fn_sel_file = [this](const arg_mouse& arg){ - _m_select_file(arg); + + auto fn_list_handler = [this](const arg_mouse& arg){ + if(event_code::mouse_down == arg.evt_code) + { + selection_.is_deselect_delayed = true; + } + else if(event_code::mouse_up == arg.evt_code) + { + selection_.is_deselect_delayed = false; +#if 0 + this->_m_delay_deselect(true); +#endif + if(_m_sync_with_selection()) + _m_display_target_filenames(); + } + else if(event_code::mouse_move == arg.evt_code) + { + if(arg.left_button) + { + selection_.is_deselect_delayed = false; + std::cout<<"MouseMove set is_deselect_delayed = true"< bool { int dira = any_cast(fs_a)->directory ? 1 : 0; @@ -327,7 +360,7 @@ namespace nana tb_file_.events().key_char.connect_unignorable([this](const arg_keyboard& arg) { if(arg.key == nana::keyboard::enter) - _m_ok(); + _m_try_select(tb_file_.caption()); }); //Don't create the combox for choose a file extension if the dialog is used for picking a directory. @@ -341,11 +374,11 @@ namespace nana btn_ok_.create(*this); btn_ok_.i18n(i18n_eval("NANA_BUTTON_OK_SHORTKEY")); - btn_ok_.events().click.connect_unignorable([this](const arg_click&) { - _m_ok(); + _m_try_select(tb_file_.caption()); }); + btn_cancel_.create(*this); btn_cancel_.i18n(i18n_eval("NANA_BUTTON_CANCEL_SHORTKEY")); @@ -377,6 +410,10 @@ namespace nana } else caption(title); + + + if(!allow_multi_select) + ls_file_.enable_single(true, true); } void def_extension(const std::string& ext) @@ -392,7 +429,7 @@ namespace nana std::string dir; auto pos = init_file.find_last_of("\\/"); - auto file_with_path_removed = (pos != init_file.npos ? init_file.substr(pos + 1) : init_file); + auto filename = (pos != init_file.npos ? init_file.substr(pos + 1) : init_file); if(saved_init_path != init_path) { @@ -400,7 +437,7 @@ namespace nana saved_init_path = init_path; //Phase 2: Check whether init_file contains a path - if(file_with_path_removed == init_file) + if(filename == init_file) { //Phase 3: Check whether init_path is empty if(init_path.size()) @@ -414,7 +451,7 @@ namespace nana _m_load_cat_path(dir.size() ? dir : fs_ext::path_user().native()); - tb_file_.caption(file_with_path_removed); + tb_file_.caption(filename); } void add_filter(const std::string& desc, const std::string& type) @@ -450,6 +487,7 @@ namespace nana cb_types_.anyobj(i, v); } +#if 0 bool file(std::string& fs) const { if(selection_.type == kind::none) @@ -464,6 +502,20 @@ namespace nana fs = selection_.target; return true; } +#endif + std::vector files() const + { + if(kind::none == selection_.type) + return {}; + + auto pos = selection_.targets.front().find_last_of("\\/"); + if(pos != selection_.targets.front().npos) + saved_selected_path = selection_.targets.front().substr(0, pos); + else + saved_selected_path.clear(); + + return selection_.targets; + } private: void _m_layout() { @@ -704,7 +756,7 @@ namespace nana if(pick_directory_) return is_dir; - if((is_dir || 0 == extension) || (0 == extension->size())) return true; + if(is_dir || (nullptr == extension) || extension->empty()) return true; for(auto & extstr : *extension) { @@ -799,9 +851,8 @@ namespace nana ls_file_.auto_draw(true); } - void _m_finish(kind::t type, const std::string& tar) + void _m_finish(kind::t type) { - selection_.target = tar; selection_.type = type; close(); } @@ -881,40 +932,346 @@ namespace nana return true; } private: - void _m_select_file(const arg_mouse& arg) + void _m_insert_filename(const std::string& name) { - auto sel = ls_file_.selected(); - if(sel.empty()) + if(selection_.targets.cend() == std::find(selection_.targets.cbegin(), selection_.targets.cend(), name)) + selection_.targets.push_back(name); + } + + bool _m_hovered_good() const + { + auto pos = ls_file_.hovered(false); + if(!pos.empty()) + { + item_fs mfs; + ls_file_.at(pos).resolve_to(mfs); + return !mfs.directory; + } + return false; + } + + bool _m_has_good_select() const + { + auto selected_items = ls_file_.selected(); + if(selected_items.size()) + { + for(auto & pos : selected_items) + { + item_fs mfs; + ls_file_.at(pos).resolve_to(mfs); + + if((mode::open_directory == mode_) || (false == mfs.directory)) + return true; + } + } + return false; + } + + bool _m_sync_with_selection() + { + if(_m_has_good_select()) + { + auto selected_items = ls_file_.selected(); + if(selected_items.size() && (selected_items.size() < selection_.targets.size())) + { + selection_.targets.clear(); + for(auto & pos : selected_items) + { + item_fs mfs; + ls_file_.at(pos).resolve_to(mfs); + + if((mode::open_directory == mode_) || (false == mfs.directory)) + selection_.targets.push_back(mfs.name); + } + return true; + } + } + return false; + } + + void _m_click_select_file(const arg_mouse& arg) + { + auto selected_item = ls_file_.hovered(false); + if(selected_item.empty()) return; - auto index = sel[0]; item_fs m; - ls_file_.at(index).resolve_to(m); + ls_file_.at(selected_item).resolve_to(m); if(event_code::dbl_click == arg.evt_code) { - if(m.directory) - _m_load_cat_path(addr_.filesystem + m.name + "/"); + if(!m.directory) + _m_try_select(m.name); else - _m_finish(kind::filesystem, addr_.filesystem + m.name); + _m_load_cat_path(addr_.filesystem + m.name + "/"); } +#if 0 else { if((mode::open_directory == mode_) || (false == m.directory)) { - selection_.target = addr_.filesystem + m.name; - tb_file_.caption(m.name); + if(ls_file_.is_single_enabled(true) || !arg.ctrl) + { + std::cout<<"assign by click select "< _m_strip_files(const std::string& text) + { + std::vector files; + std::size_t start_pos = 0; + while(true) + { + while(true) + { + auto pos = text.find_first_of(" \"", start_pos); + if(text.npos == pos) + { + if(text.length() == start_pos) + return files; + + return {}; + } + + start_pos = pos + 1; + + if(text[pos] == '"') + break; + } + + auto pos = text.find('"', start_pos); + if(text.npos == pos) + return {}; + + if(pos - start_pos > 0) + files.push_back(text.substr(start_pos, pos - start_pos)); + + start_pos = pos + 1; + } + + return files; + + } + + void _m_try_select(const std::string& files_text) + { + selection_.targets.clear(); + //auto file = tb_file_.caption(); + auto targets = _m_strip_files(files_text); +#if 1 //debug + std::cout<<"filebox OK: file="<(fattr.type()); + + //Check if the selected name is a directory + auto is_dir = fs::is_directory(fattr); + + if(!is_dir && _m_append_def_extension(tar)) + { + //Add the extension, then check if it is a directory again. + fattr = fs::status(tar); + ftype = static_cast(fattr.type()); + is_dir = fs::is_directory(fattr); + } + + if(is_dir) + { + //Enter the directory if this is the first tar. + if(0 == tar_idx) + { + _m_load_cat_path(tar); + tb_file_.caption(std::string{}); + return; + } + + //Other folders are ignored + continue; + } + + if(mode::write_file != mode_) + { + if(fs::file_type::not_found == ftype) + { + msgbox mb(*this, caption()); + mb.icon(msgbox::icon_information); + if(mode::open_file == mode_) + mb << i18n("NANA_FILEBOX_ERROR_NOT_EXISTING_AND_RETRY", tar); + else + mb << i18n("NANA_FILEBOX_ERROR_DIRECTORY_NOT_EXISTING_AND_RETRY", tar); + mb(); + + return; + } + } + else + { + if(fs::file_type::not_found != ftype) + { + msgbox mb(*this, caption(), msgbox::yes_no); + mb.icon(msgbox::icon_question); + mb<(fattr.type()); + + //Check if the selected name is a directory + auto is_dir = fs::is_directory(fattr); + + if(!is_dir && _m_append_def_extension(tar)) + { + //Add the extension, then check if it is a directory again. + fattr = fs::status(tar); + ftype = static_cast(fattr.type()); + is_dir = fs::is_directory(fattr); + } + + if(is_dir) + { + //Enter the directory if this is the first tar. + if(0 == tar_idx) + { + _m_load_cat_path(tar); + tb_file_.caption(std::string{}); + return; + } + + //Other folders are ignored + continue; + } + + if(mode::write_file != mode_) + { + if(fs::file_type::not_found == ftype) + { + msgbox mb(*this, caption()); + mb.icon(msgbox::icon_information); + if(mode::open_file == mode_) + mb << i18n("NANA_FILEBOX_ERROR_NOT_EXISTING_AND_RETRY", tar); + else + mb << i18n("NANA_FILEBOX_ERROR_DIRECTORY_NOT_EXISTING_AND_RETRY", tar); + mb(); + + return; + } + } + else + { + if(fs::file_type::not_found != ftype) + { + msgbox mb(*this, caption(), msgbox::yes_no); + mb.icon(msgbox::icon_question); + mb< targets; +#if 0 + std::vector delay_deselect; +#endif + bool is_deselect_delayed{ false }; }selection_; static std::string saved_init_path; @@ -1099,12 +1543,9 @@ namespace nana window owner; bool open_or_save; -#if defined(NANA_WINDOWS) bool allow_multi_select; std::vector files; -#else - std::string file; -#endif + std::string title; std::string path; std::vector filters; @@ -1120,8 +1561,8 @@ namespace nana { impl_->owner = owner; impl_->open_or_save = open; -#if defined(NANA_WINDOWS) impl_->allow_multi_select = false; +#if defined(NANA_WINDOWS) auto len = ::GetCurrentDirectory(0, nullptr); if(len) { @@ -1178,11 +1619,7 @@ namespace nana filebox& filebox::init_file(const std::string& ifstr) { -#if defined(NANA_WINDOWS) impl_->files = {ifstr}; -#else - impl_->file = ifstr; -#endif return *this; } @@ -1198,30 +1635,18 @@ namespace nana return impl_->path; } -#if defined(NANA_WINDOWS) - const std::string& filebox::file() const + std::string filebox::file() const { if(impl_->files.empty()) - { - static const std::string empty = ""; - return empty; - } - else - { - return impl_->files.front(); - } + return {}; + + return impl_->files.front(); } const std::vector& filebox::files() const { return impl_->files; } -#else - const std::string& filebox::file() const - { - return impl_->file; - } -#endif bool filebox::show() const { @@ -1332,7 +1757,7 @@ namespace nana #elif defined(NANA_POSIX) using mode = filebox_implement::mode; - filebox_implement fb(impl_->owner, (impl_->open_or_save ? mode::open_file : mode::write_file), impl_->title); + filebox_implement fb(impl_->owner, (impl_->open_or_save ? mode::open_file : mode::write_file), impl_->title, false, impl_->allow_multi_select); if(impl_->filters.size()) { @@ -1353,27 +1778,28 @@ namespace nana else fb.add_filter("All Files", "*.*"); - fb.load_fs(impl_->path, impl_->file); + fb.load_fs(impl_->path, this->file()); API::modal_window(fb); - if(false == fb.file(impl_->file)) + + fb.files().swap(impl_->files); + if(impl_->files.empty()) return false; - auto tpos = impl_->file.find_last_of("\\/"); - if(tpos != impl_->file.npos) - impl_->path = impl_->file.substr(0, tpos); + + auto tpos = impl_->files.front().find_last_of("\\/"); + if(tpos != impl_->files.front().npos) + impl_->path = impl_->files.front().substr(0, tpos); else impl_->path.clear(); #endif - return true; } -#if defined(NANA_WINDOWS) + void filebox::allow_multi_select(bool allow) { impl_->allow_multi_select = allow; } -#endif //end class filebox //class directory picker @@ -1484,17 +1910,18 @@ namespace nana #elif defined(NANA_POSIX) using mode = filebox_implement::mode; - filebox_implement fb(impl_->owner, mode::open_directory, {}, true); + filebox_implement fb(impl_->owner, mode::open_directory, {}, true, false/*single select*/); fb.load_fs(impl_->init_path, ""); API::modal_window(fb); std::string path_directory; - if(false == fb.file(path_directory)) + auto path_dir = fb.files(); + if(path_dir.empty()) return {}; - return path_type{path_directory}; + return path_type{path_dir.front()}; #endif } }//end namespace nana From fb8ade6557f432c6172845783e4f500cfeefdc51 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Thu, 31 Jan 2019 07:08:45 +0800 Subject: [PATCH 129/149] some modifications to #357 and #383 --- include/nana/gui/widgets/detail/compset.hpp | 8 +++-- include/nana/gui/widgets/treebox.hpp | 2 +- source/gui/widgets/listbox.cpp | 5 ++- source/gui/widgets/treebox.cpp | 37 ++++++++++++++------- 4 files changed, 35 insertions(+), 17 deletions(-) diff --git a/include/nana/gui/widgets/detail/compset.hpp b/include/nana/gui/widgets/detail/compset.hpp index 3991fe4e..8518b85c 100644 --- a/include/nana/gui/widgets/detail/compset.hpp +++ b/include/nana/gui/widgets/detail/compset.hpp @@ -1,6 +1,6 @@ /* * Concept of Component Set - * Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -60,17 +60,19 @@ namespace nana{ namespace widgets{ namespace detail /// Widget scheme. typedef WidgetScheme widget_scheme_t; - widget_scheme_t * wdg_scheme_ptr_{ nullptr }; + //widget_scheme_t * wdg_scheme_ptr_{ nullptr }; //deprecated public: /// The destructor. virtual ~compset_placer(){} + /* /// Init the scheme pointer - void init_scheme(widget_scheme_t* wdg_scheme_ptr) + void init_scheme(widget_scheme_t* wdg_scheme_ptr) //deprecated { wdg_scheme_ptr_ = wdg_scheme_ptr; } + */ /// Enable/Disable the specified component. virtual void enable(component_t, bool) = 0; diff --git a/include/nana/gui/widgets/treebox.hpp b/include/nana/gui/widgets/treebox.hpp index 4ff78e99..0d78bbde 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-2018 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE or copy at diff --git a/source/gui/widgets/listbox.cpp b/source/gui/widgets/listbox.cpp index 4b902c43..895b2829 100644 --- a/source/gui/widgets/listbox.cpp +++ b/source/gui/widgets/listbox.cpp @@ -6195,8 +6195,9 @@ namespace nana return; std::vector new_idx; for(size_type i=first_col; i<=last_col; ++i) new_idx.push_back(i); - const item_proxy & ip_row=this->at(row); + internal_scope_guard lock; + const item_proxy ip_row = this->at(row); const nana::any *pnany=_m_ess().lister.anyobj(row,false); std::sort(new_idx.begin(), new_idx.end(), [&](size_type col1, size_type col2) @@ -6205,6 +6206,8 @@ namespace nana ip_row.text(col2), col2, pnany, reverse); }); + + //Only change the view position of columns for(size_t i=0; icrook_size : 0), (enable_icon_ ? wdg_scheme_ptr_->icon_size : 0)); + auto m = std::max((enable_crook_ ? scheme_.crook_size : 0), (enable_icon_ ? scheme_.icon_size : 0)); + +#if 1 + unsigned as = 0, ds = 0, il; + graph.text_metrics(as, ds, il); + return std::max(as + ds + 8, m); +#else #ifdef _nana_std_has_string_view return std::max(m, graph.text_extent_size(std::wstring_view{ L"jH{", 3 }).height + 8); #else return std::max(m, graph.text_extent_size(L"jH{", 3).height + 8); +#endif #endif } virtual unsigned item_width(graph_reference graph, const item_attribute_t& attr) const override { - return graph.text_extent_size(attr.text).width + (enable_crook_ ? wdg_scheme_ptr_->crook_size : 0) + (enable_icon_ ? wdg_scheme_ptr_ ->icon_size: 0) + (wdg_scheme_ptr_->text_offset << 1) + wdg_scheme_ptr_->item_offset; + return graph.text_extent_size(attr.text).width + (enable_crook_ ? scheme_.crook_size : 0) + (enable_icon_ ? scheme_.icon_size : 0) + (scheme_.text_offset << 1) + scheme_.item_offset; } // Locate a component through the specified coordinate. @@ -1389,7 +1400,7 @@ namespace nana case component_t::expander: if(attr.has_children) { - r->width = wdg_scheme_ptr_->item_offset; + r->width = scheme_.item_offset; return true; } return false; @@ -1398,26 +1409,26 @@ namespace nana case component_t::crook: if(enable_crook_) { - r->x += wdg_scheme_ptr_->item_offset; - r->width = wdg_scheme_ptr_->crook_size; + r->x += scheme_.item_offset; + r->width = scheme_.crook_size; return true; } return false; case component_t::icon: if(enable_icon_) { - r->x += wdg_scheme_ptr_->item_offset + (enable_crook_ ? wdg_scheme_ptr_->crook_size : 0); + r->x += scheme_.item_offset + (enable_crook_ ? scheme_.crook_size : 0); r->y = 2; - r->width = wdg_scheme_ptr_->icon_size; + r->width = scheme_.icon_size; r->height -= 2; return true; } return false; case component_t::text: { - auto text_pos = wdg_scheme_ptr_->item_offset + (enable_crook_ ? wdg_scheme_ptr_->crook_size : 0) + (enable_icon_ ? wdg_scheme_ptr_->icon_size : 0) + wdg_scheme_ptr_->text_offset; + auto text_pos = scheme_.item_offset + (enable_crook_ ? scheme_.crook_size : 0) + (enable_icon_ ? scheme_.icon_size : 0) + scheme_.text_offset; r->x += text_pos; - r->width -= (text_pos + wdg_scheme_ptr_->text_offset); + r->width -= (text_pos + scheme_.text_offset); }; return true; default: @@ -1426,6 +1437,7 @@ namespace nana return false; } private: + const scheme& scheme_; bool enable_crook_{ false }; bool enable_icon_{ false }; }; @@ -1680,7 +1692,7 @@ namespace nana { impl_->data.trigger_ptr = this; impl_->data.renderer = nana::pat::cloneable(internal_renderer()); - impl_->data.comp_placer = nana::pat::cloneable(internal_placer()); + //impl_->data.comp_placer = nana::pat::cloneable(internal_placer()); //deprecated impl_->adjust.timer.elapse([this] { @@ -1946,7 +1958,8 @@ namespace nana widget.bgcolor(colors::white); impl_->data.widget_ptr = static_cast<::nana::treebox*>(&widget); impl_->data.scheme_ptr = static_cast<::nana::treebox::scheme_type*>(API::dev::get_scheme(widget)); - impl_->data.comp_placer->init_scheme(impl_->data.scheme_ptr); + //impl_->data.comp_placer->init_scheme(impl_->data.scheme_ptr); //deprecated + impl_->data.comp_placer = nana::pat::cloneable(internal_placer{ *impl_->data.scheme_ptr }); widget.caption("nana treebox"); } From 85c2bdbd75586dac58b1626c9fbc39e2bfaaa27e Mon Sep 17 00:00:00 2001 From: Jinhao Date: Sat, 2 Feb 2019 00:31:56 +0800 Subject: [PATCH 130/149] small changes --- source/gui/widgets/checkbox.cpp | 2 +- source/gui/widgets/listbox.cpp | 4 ++-- source/gui/widgets/spinbox.cpp | 2 +- source/gui/widgets/treebox.cpp | 2 ++ 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/source/gui/widgets/checkbox.cpp b/source/gui/widgets/checkbox.cpp index 3148ee88..fc08fcac 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-2018 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at diff --git a/source/gui/widgets/listbox.cpp b/source/gui/widgets/listbox.cpp index 895b2829..13baf15a 100644 --- a/source/gui/widgets/listbox.cpp +++ b/source/gui/widgets/listbox.cpp @@ -6197,8 +6197,8 @@ namespace nana for(size_type i=first_col; i<=last_col; ++i) new_idx.push_back(i); internal_scope_guard lock; - const item_proxy ip_row = this->at(row); - const nana::any *pnany=_m_ess().lister.anyobj(row,false); + auto ip_row = this->at(row); + auto pnany=_m_ess().lister.anyobj(row,false); std::sort(new_idx.begin(), new_idx.end(), [&](size_type col1, size_type col2) { diff --git a/source/gui/widgets/spinbox.cpp b/source/gui/widgets/spinbox.cpp index 49ae0347..4a631c2c 100644 --- a/source/gui/widgets/spinbox.cpp +++ b/source/gui/widgets/spinbox.cpp @@ -1,7 +1,7 @@ /* * A Spin box widget * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at diff --git a/source/gui/widgets/treebox.cpp b/source/gui/widgets/treebox.cpp index 4181177e..e5be4dcd 100644 --- a/source/gui/widgets/treebox.cpp +++ b/source/gui/widgets/treebox.cpp @@ -1966,6 +1966,8 @@ namespace nana void trigger::detached() { + //Reset the comp_placer, because after deteching, the scheme refered by comp_placer will be released + impl_->data.comp_placer.reset(); impl_->data.graph = nullptr; } From 56065bb2b8a2a8d11f9e554651afc77151f8803a Mon Sep 17 00:00:00 2001 From: besh81 Date: Wed, 6 Feb 2019 11:35:33 +0100 Subject: [PATCH 131/149] fixed listbox scheme.header_padding_top use --- source/gui/widgets/listbox.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/source/gui/widgets/listbox.cpp b/source/gui/widgets/listbox.cpp index 13baf15a..28f8c409 100644 --- a/source/gui/widgets/listbox.cpp +++ b/source/gui/widgets/listbox.cpp @@ -3511,12 +3511,9 @@ namespace nana //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 + static_cast(essence_->scheme_ptr->header_padding_top) }; if (align::left == column.alignment) From bbc39906c00950b537d40876e433de412ef408bb Mon Sep 17 00:00:00 2001 From: Jinhao Date: Fri, 15 Feb 2019 06:27:17 +0800 Subject: [PATCH 132/149] remove deprecated code and debug code --- include/nana/datetime.hpp | 2 +- include/nana/gui/effects.hpp | 2 +- include/nana/gui/widgets/detail/compset.hpp | 11 - include/nana/gui/widgets/treebox.hpp | 21 -- source/detail/platform_spec_posix.cpp | 69 +---- source/detail/posix/msg_dispatcher.hpp | 6 - source/detail/posix/theme.cpp | 14 - source/detail/posix/xdnd_protocol.hpp | 91 +------ source/gui/detail/bedrock_posix.cpp | 2 - source/gui/detail/drawer.cpp | 2 +- source/gui/detail/native_window_interface.cpp | 17 -- source/gui/dragdrop.cpp | 99 +------ source/gui/filebox.cpp | 256 +----------------- source/gui/widgets/listbox.cpp | 9 +- source/gui/widgets/skeletons/text_editor.cpp | 1 - source/gui/widgets/treebox.cpp | 40 --- 16 files changed, 28 insertions(+), 614 deletions(-) diff --git a/include/nana/datetime.hpp b/include/nana/datetime.hpp index 3abe9500..c87d58a3 100644 --- a/include/nana/datetime.hpp +++ b/include/nana/datetime.hpp @@ -1,6 +1,6 @@ /* * A Date Time Implementation - * Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at diff --git a/include/nana/gui/effects.hpp b/include/nana/gui/effects.hpp index c7a8f0a4..c405f8ef 100644 --- a/include/nana/gui/effects.hpp +++ b/include/nana/gui/effects.hpp @@ -1,6 +1,6 @@ /* * Background Effects Implementation - * Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at diff --git a/include/nana/gui/widgets/detail/compset.hpp b/include/nana/gui/widgets/detail/compset.hpp index 8518b85c..363bb6a7 100644 --- a/include/nana/gui/widgets/detail/compset.hpp +++ b/include/nana/gui/widgets/detail/compset.hpp @@ -59,21 +59,10 @@ namespace nana{ namespace widgets{ namespace detail /// Widget scheme. typedef WidgetScheme widget_scheme_t; - - //widget_scheme_t * wdg_scheme_ptr_{ nullptr }; //deprecated - public: /// The destructor. virtual ~compset_placer(){} - /* - /// Init the scheme pointer - void init_scheme(widget_scheme_t* wdg_scheme_ptr) //deprecated - { - wdg_scheme_ptr_ = wdg_scheme_ptr; - } - */ - /// Enable/Disable the specified component. virtual void enable(component_t, bool) = 0; virtual bool enabled(component_t) const = 0; diff --git a/include/nana/gui/widgets/treebox.hpp b/include/nana/gui/widgets/treebox.hpp index 0d78bbde..ba40c4c1 100644 --- a/include/nana/gui/widgets/treebox.hpp +++ b/include/nana/gui/widgets/treebox.hpp @@ -105,15 +105,8 @@ namespace nana class trigger :public drawer_trigger { - //template - //struct basic_implement; //deprecated - class implementation; - - //class item_renderer; //deprecated class item_locator; - - //typedef basic_implement implement; //deprecated public: struct treebox_node_type { @@ -142,17 +135,12 @@ namespace nana pat::cloneable& renderer() const; - //void renderer(::nana::pat::cloneable&&); - //const ::nana::pat::cloneable& renderer() const; //deprecated void placer(::nana::pat::cloneable&&); const ::nana::pat::cloneable& placer() const; node_type* insert(node_type*, const std::string& key, std::string&&); node_type* insert(const std::string& path, std::string&&); - //node_type * selected() const; //deprecated - //void selected(node_type*); - node_image_tag& icon(const ::std::string&); void icon_erase(const ::std::string&); void node_icon(node_type*, const ::std::string& id); @@ -314,15 +302,6 @@ namespace nana return *p; } - /* - template - item_proxy & value(const T& t) //deprecated - { - _m_value() = t; - return *this; - } - */ - template item_proxy & value(T&& t) { diff --git a/source/detail/platform_spec_posix.cpp b/source/detail/platform_spec_posix.cpp index f607e9a3..bca68ea8 100644 --- a/source/detail/platform_spec_posix.cpp +++ b/source/detail/platform_spec_posix.cpp @@ -36,8 +36,6 @@ #include "posix/msg_dispatcher.hpp" -#include //debug - namespace nana { namespace detail @@ -1121,12 +1119,7 @@ namespace detail platform_scope_guard lock; if(0 != xdnd_.dragdrop.count(wd)) return false; -#if 0 //deprecated - std::cout<<"ChangeProperty XdndAware"<(wd), atombase_.xdnd_aware, XA_ATOM, sizeof(int) * 8, - PropModeReplace, reinterpret_cast(&dndver), 1); -#endif + xdnd_.dragdrop[wd] = ddrop; return true; } @@ -1140,7 +1133,6 @@ namespace detail new_val = (xdnd_.targets[wd] += count); if(1 == new_val) { - std::cout<<"ChangeProperty XdndAware"<(wd), atombase_.xdnd_aware, XA_ATOM, sizeof(int) * 8, PropModeReplace, reinterpret_cast(&dndver), 1); @@ -1235,7 +1227,6 @@ namespace detail } else if(evt.xselection.property == self.atombase_.xdnd_selection) { - std::cout<<"Platform SelectionNotfy by XdndSelection, type="<push_back(file); } - std::cout<<" file size:"<size()<size()) { msg.u.mouse_drop.window = evt.xselection.requestor; @@ -1285,8 +1274,6 @@ namespace detail accepted = true; ::XFree(data); } - else - std::cout<<" invalid property"<(&(evt.xclient.data.l[2])); unsigned long len = 3; unsigned char * data = 0; @@ -1380,32 +1360,19 @@ namespace detail Atom type; int format; unsigned long bytes_left; - auto status = ::XGetWindowProperty(self.display_, self.xdnd_.wd_src, self.atombase_.xdnd_typelist, 0, 0, False, + ::XGetWindowProperty(self.display_, self.xdnd_.wd_src, self.atombase_.xdnd_typelist, 0, 0, False, XA_ATOM, &type, &format, &len, &bytes_left, &data); - std::cout<<", XGetWindowProperty xdnd_typelist = "<> 16); int y = (evt.xclient.data.l[2] & 0xFFFF); @@ -1482,7 +1433,6 @@ namespace detail Window child; ::XTranslateCoordinates(self.display_, self.root_window(), evt.xclient.window, x, y, &self.xdnd_.pos.x, &self.xdnd_.pos.y, &child); - std::cout<<" find_window"<(evt.xclient.window), self.xdnd_.pos); if(wd && wd->flags.dropable) { @@ -1491,8 +1441,6 @@ namespace detail self.xdnd_.pos = wd->pos_root; accepted = 1; } - - std::cout<<" return,"< #include -#include //debug - namespace nana { namespace detail @@ -231,10 +229,6 @@ namespace detail if(keymap[event.xkey.keycode / 8] & (1 << (event.xkey.keycode % 8))) continue; } - else if(SelectionRequest == event.type) - { - std::cout<<"Dispatcher SelectionRequest"< #include -#include //debug namespace nana { namespace detail @@ -179,13 +178,7 @@ namespace nana auto name = find_kde_theme_name(); if(name.empty()) - { - name = find_gnome_theme_name(); - std::cout<<"GNOME:"< directories_; }; - void test() - { - theme thm; - auto png = thm.icon("folder", 30); - std::cout<<"Icon Theme="< -#define DEBUG_XDND_PROTOCOL - -#ifdef DEBUG_XDND_PROTOCOL -#include //debug -#endif - namespace nana{ namespace detail { @@ -58,15 +52,6 @@ namespace nana{ detail::platform_scope_guard lock; ::XSetSelectionOwner(disp, spec_.atombase().xdnd_selection, source, CurrentTime); -#ifdef DEBUG_XDND_PROTOCOL - std::cout<<"XSetSelectionOwner "<(xclient.data.l[0]); bool is_accepted_by_target = (xclient.data.l[1] & 1); -#ifdef DEBUG_XDND_PROTOCOL - std::cout<<"XdndStatus: Accepted="< #include -#include //debug namespace nana { @@ -445,7 +444,6 @@ namespace detail case nana::detail::msg_packet_tag::pkt_family::mouse_drop: msgwd = brock.wd_manager().find_window(native_window, {msg.u.mouse_drop.x, msg.u.mouse_drop.y}); - std::cout<<" MouseDrop msgwd="<& evt_codes, const bool bDisabled) { - for (const auto& evt_code : evt_codes) + for (auto evt_code : evt_codes) { filter_event(evt_code, bDisabled); } diff --git a/source/gui/detail/native_window_interface.cpp b/source/gui/detail/native_window_interface.cpp index cf01e33d..a9753bc7 100644 --- a/source/gui/detail/native_window_interface.cpp +++ b/source/gui/detail/native_window_interface.cpp @@ -955,14 +955,6 @@ namespace nana{ auto fm_extents = window_frame_extents(wd); origin.x = -fm_extents.left; origin.y = -fm_extents.top; -#if 0 //deprecated - if(reinterpret_cast(coord_wd) != restrict::spec.root_window()) - { - fm_extents = window_frame_extents(coord_wd); - origin.x += fm_extents.left; - origin.y += fm_extents.top; - } -#endif } else coord_wd = get_window(wd, window_relationship::parent); @@ -1601,10 +1593,6 @@ namespace nana{ if(True == ::XTranslateCoordinates(restrict::spec.open_display(), reinterpret_cast(wd), restrict::spec.root_window(), x, y, &pos.x, &pos.y, &child)) { - //deprecated - //auto fm_extents = window_frame_extents(wd); - //pos.x += fm_extents.left; - //pos.y += fm_extents.top; return true; } #endif @@ -1627,11 +1615,6 @@ namespace nana{ Window child; if(True == ::XTranslateCoordinates(restrict::spec.open_display(), restrict::spec.root_window(), reinterpret_cast(wd), x, y, &pos.x, &pos.y, &child)) { - //deprecated - //Now the origin of pos is the left-top corner of the window(including titlebar and border) - //auto fm_extents = window_frame_extents(wd); - //pos.x += fm_extents.left; - //pos.y += fm_extents.top; return true; } #endif diff --git a/source/gui/dragdrop.cpp b/source/gui/dragdrop.cpp index 2c2d394a..18f51e52 100644 --- a/source/gui/dragdrop.cpp +++ b/source/gui/dragdrop.cpp @@ -308,66 +308,6 @@ namespace nana class win32_dropdata : public win32com_iunknown { -#if 0 //deprecated - class enumer : public win32com_iunknown - { - public: - enumer(drop_data& data) : - data_(data) - {} - - enumer(const enumer& rhs): - data_(rhs.data_), - cursor_(rhs.cursor_) - { - } - private: - // Implement IEnumFORMATETC - HRESULT Clone(IEnumFORMATETC **ppenum) override - { - *ppenum = new enumer{ *this }; - return S_OK; - } - - HRESULT Next(ULONG celt, FORMATETC *rgelt, ULONG *pceltFetched) override - { - HRESULT result = (cursor_ + celt <= data_.entries_.size() ? S_OK : S_FALSE); - - auto const fetched = (std::min)(std::size_t(celt), data_.entries_.size() - cursor_); - - for (std::size_t i = 0; i < fetched; ++i) - *rgelt++ = data_.entries_[cursor_++]->format; - - if (pceltFetched) - *pceltFetched = static_cast(fetched); - else if (celt > 1) - return S_FALSE; - - return (celt == fetched ? S_OK : S_FALSE); - } - - HRESULT Reset() override - { - cursor_ = 0; - return S_OK; - } - - HRESULT Skip(ULONG celt) override - { - if (cursor_ + celt < data_.entries_.size()) - { - cursor_ += celt; - return S_OK; - } - - cursor_ = data_.entries_.size(); - return S_FALSE; - } - private: - drop_data & data_; - std::size_t cursor_; - }; -#endif public: struct data_entry { @@ -625,8 +565,6 @@ namespace nana #elif defined(NANA_X11) - constexpr int xdnd_version = 5; - class x11_dropdata { public: @@ -839,17 +777,13 @@ namespace nana auto cur_wd = API::find_window(API::cursor_position()); - std::cout<<" Hovered="<has(drag_wd, cur_wd)) ? "dnd-move" : "dnd-none"); - std::cout<<" ICON="<(window_handle); + auto real_wd = reinterpret_cast(window_handle); real_wd->other.dnd_state = dragdrop_status::not_ready; } @@ -1072,8 +1001,7 @@ namespace nana { impl_->dragging = ((!impl_->predicate) || impl_->predicate()); - using basic_window = ::nana::detail::basic_window; - auto real_wd = reinterpret_cast<::nana::detail::basic_window*>(impl_->window_handle); + auto real_wd = reinterpret_cast(impl_->window_handle); real_wd->other.dnd_state = dragdrop_status::ready; } }); @@ -1082,8 +1010,7 @@ namespace nana if (!(arg.is_left_button() && impl_->dragging && API::is_window(arg.window_handle))) return; - using basic_window = ::nana::detail::basic_window; - auto real_wd = reinterpret_cast<::nana::detail::basic_window*>(arg.window_handle); + auto real_wd = reinterpret_cast(arg.window_handle); real_wd->other.dnd_state = dragdrop_status::in_progress; std::unique_ptr dropdata{new dragdrop_service::dropdata_type}; @@ -1190,8 +1117,7 @@ namespace nana { impl_->dragging = ((!impl_->predicate) || impl_->predicate()); - using basic_window = ::nana::detail::basic_window; - auto real_wd = reinterpret_cast<::nana::detail::basic_window*>(impl_->source_handle); + auto real_wd = reinterpret_cast(impl_->source_handle); real_wd->other.dnd_state = dragdrop_status::ready; } }); @@ -1200,27 +1126,14 @@ namespace nana if (!(arg.is_left_button() && impl_->dragging && API::is_window(arg.window_handle))) return; - using basic_window = ::nana::detail::basic_window; - auto real_wd = reinterpret_cast<::nana::detail::basic_window*>(arg.window_handle); + auto real_wd = reinterpret_cast(arg.window_handle); real_wd->other.dnd_state = dragdrop_status::in_progress; impl_->make_drop(); - //deprecated - //auto has_dropped = dragdrop_service::instance().dragdrop(arg.window_handle); real_wd->other.dnd_state = dragdrop_status::not_ready; impl_->dragging = false; - - /* //deprecated - if (has_dropped) - { - auto drop_wd = API::find_window(API::cursor_position()); - //auto i = impl_->targets.find(drop_wd); - //if ((impl_->targets.end() != i) && i->second) - // i->second(); - } - */ }); } diff --git a/source/gui/filebox.cpp b/source/gui/filebox.cpp index a8848e1e..b830b8e5 100644 --- a/source/gui/filebox.cpp +++ b/source/gui/filebox.cpp @@ -250,19 +250,14 @@ namespace nana else if(event_code::mouse_up == arg.evt_code) { selection_.is_deselect_delayed = false; -#if 0 - this->_m_delay_deselect(true); -#endif + if(_m_sync_with_selection()) _m_display_target_filenames(); } else if(event_code::mouse_move == arg.evt_code) { if(arg.left_button) - { selection_.is_deselect_delayed = false; - std::cout<<"MouseMove set is_deselect_delayed = true"< files() const { if(kind::none == selection_.type) @@ -1005,53 +984,8 @@ namespace nana else _m_load_cat_path(addr_.filesystem + m.name + "/"); } -#if 0 - else - { - if((mode::open_directory == mode_) || (false == m.directory)) - { - if(ls_file_.is_single_enabled(true) || !arg.ctrl) - { - std::cout<<"assign by click select "<(fattr.type()); - - //Check if the selected name is a directory - auto is_dir = fs::is_directory(fattr); - - if(!is_dir && _m_append_def_extension(tar)) - { - //Add the extension, then check if it is a directory again. - fattr = fs::status(tar); - ftype = static_cast(fattr.type()); - is_dir = fs::is_directory(fattr); - } - - if(is_dir) - { - _m_load_cat_path(tar); - tb_file_.caption(std::string{}); - return; - } - - if(mode::write_file != mode_) - { - if(fs::file_type::not_found == ftype) - { - msgbox mb(*this, caption()); - mb.icon(msgbox::icon_information); - if(mode::open_file == mode_) - mb << i18n("NANA_FILEBOX_ERROR_NOT_EXISTING_AND_RETRY", tar); - else - mb << i18n("NANA_FILEBOX_ERROR_DIRECTORY_NOT_EXISTING_AND_RETRY", tar); - mb(); - - return; - } - } - else - { - if(fs::file_type::not_found != ftype) - { - msgbox mb(*this, caption(), msgbox::yes_no); - mb.icon(msgbox::icon_question); - mb<(fattr.type()); - - //Check if the selected name is a directory - auto is_dir = fs::is_directory(fattr); - - if(!is_dir && _m_append_def_extension(tar)) - { - //Add the extension, then check if it is a directory again. - fattr = fs::status(tar); - ftype = static_cast(fattr.type()); - is_dir = fs::is_directory(fattr); - } - - if(is_dir) - { - //Enter the directory if this is the first tar. - if(0 == tar_idx) - { - _m_load_cat_path(tar); - tb_file_.caption(std::string{}); - return; - } - - //Other folders are ignored - continue; - } - - if(mode::write_file != mode_) - { - if(fs::file_type::not_found == ftype) - { - msgbox mb(*this, caption()); - mb.icon(msgbox::icon_information); - if(mode::open_file == mode_) - mb << i18n("NANA_FILEBOX_ERROR_NOT_EXISTING_AND_RETRY", tar); - else - mb << i18n("NANA_FILEBOX_ERROR_DIRECTORY_NOT_EXISTING_AND_RETRY", tar); - mb(); - - return; - } - } - else - { - if(fs::file_type::not_found != ftype) - { - msgbox mb(*this, caption(), msgbox::yes_no); - mb.icon(msgbox::icon_question); - mb< targets; -#if 0 - std::vector delay_deselect; -#endif bool is_deselect_delayed{ false }; }selection_; diff --git a/source/gui/widgets/listbox.cpp b/source/gui/widgets/listbox.cpp index 28f8c409..29105225 100644 --- a/source/gui/widgets/listbox.cpp +++ b/source/gui/widgets/listbox.cpp @@ -144,8 +144,6 @@ namespace nana const std::string&, nana::any*, bool reverse)> weak_ordering; std::shared_ptr font; ///< The exclusive column font - - column() = default; column(const column&) = default; @@ -6186,10 +6184,9 @@ namespace nana const nana::any *rowval, bool reverse)> comp) { - if (first_col<0 || last_col<=first_col) - return; - if (last_col >= column_size()) + if (last_col <= first_col || last_col >= column_size()) return; + std::vector new_idx; for(size_type i=first_col; i<=last_col; ++i) new_idx.push_back(i); @@ -6207,7 +6204,7 @@ namespace nana //Only change the view position of columns for(size_t i=0; ishape; - switch(affect) { case 0: break; @@ -1692,7 +1682,6 @@ namespace nana { impl_->data.trigger_ptr = this; impl_->data.renderer = nana::pat::cloneable(internal_renderer()); - //impl_->data.comp_placer = nana::pat::cloneable(internal_placer()); //deprecated impl_->adjust.timer.elapse([this] { @@ -1827,18 +1816,6 @@ namespace nana } } - /* //deprecated - void trigger::renderer(::nana::pat::cloneable&& r) - { - impl_->data.renderer = std::move(r); - } - - const ::nana::pat::cloneable& trigger::renderer() const - { - return impl_->data.renderer; - } - */ - ::nana::pat::cloneable& trigger::renderer() const { return impl_->data.renderer; @@ -1876,21 +1853,6 @@ namespace nana return x; } - /* - trigger::node_type* trigger::selected() const //deprecated - { - return impl_->node_state.selected; - } - */ - - /* - void trigger::selected(node_type* node) //deprecated - { - if(impl_->attr.tree_cont.verify(node) && impl_->set_selected(node)) - impl_->draw(true); - } - */ - node_image_tag& trigger::icon(const std::string& id) { auto i = impl_->shape.image_table.find(id); @@ -1958,7 +1920,6 @@ namespace nana widget.bgcolor(colors::white); impl_->data.widget_ptr = static_cast<::nana::treebox*>(&widget); impl_->data.scheme_ptr = static_cast<::nana::treebox::scheme_type*>(API::dev::get_scheme(widget)); - //impl_->data.comp_placer->init_scheme(impl_->data.scheme_ptr); //deprecated impl_->data.comp_placer = nana::pat::cloneable(internal_placer{ *impl_->data.scheme_ptr }); widget.caption("nana treebox"); @@ -2386,7 +2347,6 @@ namespace nana treebox::item_proxy treebox::selected() const { - //return item_proxy(const_cast(&get_drawer_trigger()), get_drawer_trigger().selected()); //deprecated auto dw = &get_drawer_trigger(); return item_proxy(const_cast(dw), dw->impl()->node_state.selected); } From e89ee5d18b6bb0c49273b85572098b2c2ed5bb48 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Sat, 16 Feb 2019 00:55:02 +0800 Subject: [PATCH 133/149] some improvements improved resizing performance removed frame widget refactoring --- build/bakefile/nana.bkl | 1 - build/codeblocks/nana.cbp | 1 - build/vc2013/nana.vcxproj | 2 - build/vc2013/nana.vcxproj.filters | 6 - build/vc2015/nana.vcxproj | 1 - build/vc2015/nana.vcxproj.filters | 3 - build/vc2017/nana.vcxproj | 4 +- build/vc2017/nana.vcxproj.filters | 6 - include/nana/config.hpp | 6 +- include/nana/gui/basis.hpp | 8 +- include/nana/gui/detail/basic_window.hpp | 25 +-- include/nana/gui/detail/bedrock_pi_data.hpp | 38 ----- include/nana/gui/detail/window_manager.hpp | 9 +- include/nana/gui/programming_interface.hpp | 11 +- include/nana/gui/widgets/frame.hpp | 56 ------- include/nana/gui/widgets/widget.hpp | 68 +------- source/gui/detail/basic_window.cpp | 37 +---- source/gui/detail/bedrock_pi.cpp | 111 ++++++++++++-- source/gui/detail/bedrock_posix.cpp | 27 +++- source/gui/detail/bedrock_types.hpp | 101 ++++++++++++ source/gui/detail/bedrock_windows.cpp | 38 +++-- source/gui/detail/window_layout.cpp | 19 +-- source/gui/detail/window_manager.cpp | 162 +++++--------------- source/gui/programming_interface.cpp | 37 +---- source/gui/widgets/frame.cpp | 51 ------ 25 files changed, 307 insertions(+), 521 deletions(-) delete mode 100644 include/nana/gui/detail/bedrock_pi_data.hpp delete mode 100644 include/nana/gui/widgets/frame.hpp create mode 100644 source/gui/detail/bedrock_types.hpp delete mode 100644 source/gui/widgets/frame.cpp diff --git a/build/bakefile/nana.bkl b/build/bakefile/nana.bkl index def827a4..2d7ff43f 100644 --- a/build/bakefile/nana.bkl +++ b/build/bakefile/nana.bkl @@ -62,7 +62,6 @@ library nana { gui/widgets/date_chooser.cpp gui/widgets/float_listbox.cpp gui/widgets/form.cpp - gui/widgets/frame.cpp gui/widgets/label.cpp gui/widgets/listbox.cpp gui/widgets/menubar.cpp diff --git a/build/codeblocks/nana.cbp b/build/codeblocks/nana.cbp index d48270ec..188c964d 100644 --- a/build/codeblocks/nana.cbp +++ b/build/codeblocks/nana.cbp @@ -88,7 +88,6 @@ - diff --git a/build/vc2013/nana.vcxproj b/build/vc2013/nana.vcxproj index 4051495b..7774a855 100644 --- a/build/vc2013/nana.vcxproj +++ b/build/vc2013/nana.vcxproj @@ -224,7 +224,6 @@ - @@ -325,7 +324,6 @@ - diff --git a/build/vc2013/nana.vcxproj.filters b/build/vc2013/nana.vcxproj.filters index 4c14b4d7..7337c804 100644 --- a/build/vc2013/nana.vcxproj.filters +++ b/build/vc2013/nana.vcxproj.filters @@ -147,9 +147,6 @@ Source Files\nana\gui\widgets - - Source Files\nana\gui\widgets - Source Files\nana\gui\widgets @@ -364,9 +361,6 @@ Header Files\gui\widgets - - Header Files\gui\widgets - Header Files\gui\widgets diff --git a/build/vc2015/nana.vcxproj b/build/vc2015/nana.vcxproj index f271092c..37189fc6 100644 --- a/build/vc2015/nana.vcxproj +++ b/build/vc2015/nana.vcxproj @@ -218,7 +218,6 @@ - diff --git a/build/vc2015/nana.vcxproj.filters b/build/vc2015/nana.vcxproj.filters index c0cfcbdc..8c2e213f 100644 --- a/build/vc2015/nana.vcxproj.filters +++ b/build/vc2015/nana.vcxproj.filters @@ -141,9 +141,6 @@ Source Files\gui\widgets - - Source Files\gui\widgets - Source Files\gui\widgets diff --git a/build/vc2017/nana.vcxproj b/build/vc2017/nana.vcxproj index 4e850cc2..85ad8a03 100644 --- a/build/vc2017/nana.vcxproj +++ b/build/vc2017/nana.vcxproj @@ -23,7 +23,7 @@ {42D0520F-EFA5-4831-84FE-2B9085301C5D} Win32Proj nana - 10.0.17134.0 + 10.0.14393.0 @@ -201,7 +201,6 @@ - @@ -271,7 +270,6 @@ - diff --git a/build/vc2017/nana.vcxproj.filters b/build/vc2017/nana.vcxproj.filters index 65431a04..e88f7ed3 100644 --- a/build/vc2017/nana.vcxproj.filters +++ b/build/vc2017/nana.vcxproj.filters @@ -151,9 +151,6 @@ Sources\gui\widgets - - Sources\gui\widgets - Sources\gui\widgets @@ -366,9 +363,6 @@ Include\gui\widgets - - Include\gui\widgets - Include\gui diff --git a/include/nana/config.hpp b/include/nana/config.hpp index aac360c4..1d88b379 100644 --- a/include/nana/config.hpp +++ b/include/nana/config.hpp @@ -1,7 +1,7 @@ /** * Nana Configuration * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -25,10 +25,6 @@ #include "c++defines.hpp" -//This marco is defined since 1.4 and until 1.5 for deprecating frame widget. -//This marco and class frame will be removed in version 1.5 -#define WIDGET_FRAME_DEPRECATED - //The following basic configurations are ignored when NANA_IGNORE_CONF is defined. //The NANA_IGNORE_CONF may be specified by CMake generated makefile. #ifndef NANA_IGNORE_CONF diff --git a/include/nana/gui/basis.hpp b/include/nana/gui/basis.hpp index f7ed845d..1e043621 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-2018 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -80,17 +80,11 @@ namespace nana widget = 0x1, lite_widget = 0x3, root = 0x5 -#ifndef WIDGET_FRAME_DEPRECATED - ,frame = 0x9 -#endif }; //wait for constexpr struct widget_tag{ static const flags value = flags::widget; }; struct lite_widget_tag : public widget_tag{ static const flags value = flags::lite_widget; }; struct root_tag : public widget_tag{ static const flags value = flags::root; }; -#ifndef WIDGET_FRAME_DEPRECATED - struct frame_tag : public widget_tag{ static const flags value = flags::frame; }; -#endif }// end namespace category using native_window_type = detail::native_window_handle_impl*; diff --git a/include/nana/gui/detail/basic_window.hpp b/include/nana/gui/detail/basic_window.hpp index 5911b598..b79ec168 100644 --- a/include/nana/gui/detail/basic_window.hpp +++ b/include/nana/gui/detail/basic_window.hpp @@ -1,7 +1,7 @@ /** * A Basic Window Widget Definition * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -116,10 +116,6 @@ namespace detail /// bind a native window and baisc_window void bind_native_window(native_window_type, unsigned width, unsigned height, unsigned extra_width, unsigned extra_height, paint::graphics&); -#ifndef WIDGET_FRAME_DEPRECATED - void frame_window(native_window_type); -#endif - bool is_ancestor_of(const basic_window* wd) const; bool visible_parents() const; bool displayed() const; @@ -180,7 +176,8 @@ namespace detail bool ignore_mouse_focus : 1; ///< A flag indicates whether the widget accepts focus when clicking on it bool space_click_enabled : 1; ///< A flag indicates whether enable mouse_down/click/mouse_up when pressing and releasing whitespace key. bool draggable : 1; - unsigned Reserved :17; + bool ignore_child_mapping : 1; + unsigned Reserved :16; unsigned char tab; ///< indicate a window that can receive the keyboard TAB mouse_action action; mouse_action action_before; @@ -206,19 +203,8 @@ namespace detail struct other_tag { -#ifndef WIDGET_FRAME_DEPRECATED - struct attr_frame_tag - { - native_window_type container{nullptr}; - std::vector attach; - }; -#endif - struct attr_root_tag { -#ifndef WIDGET_FRAME_DEPRECATED - container frames; ///< initialization is null, it will be created while creating a frame widget. Refer to WindowManager::create_frame -#endif container tabstop; std::vector effects_edge_nimbus; basic_window* focus{nullptr}; @@ -237,12 +223,11 @@ namespace detail update_state upd_state; dragdrop_status dnd_state{ dragdrop_status::not_ready }; + container mapping_requester; ///< Children which are ignored to mapping + union { attr_root_tag * root; -#ifndef WIDGET_FRAME_DEPRECATED - attr_frame_tag * frame; -#endif }attribute; other_tag(category::flags); diff --git a/include/nana/gui/detail/bedrock_pi_data.hpp b/include/nana/gui/detail/bedrock_pi_data.hpp deleted file mode 100644 index 7404777b..00000000 --- a/include/nana/gui/detail/bedrock_pi_data.hpp +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef NANA_DETAIL_BEDROCK_PI_DATA_HPP -#define NANA_DETAIL_BEDROCK_PI_DATA_HPP - -#include - -#include -#include "color_schemes.hpp" -#include "events_operation.hpp" -#include "window_manager.hpp" -#include - -namespace nana -{ - namespace detail - { - struct bedrock::pi_data - { - color_schemes scheme; - events_operation evt_operation; - window_manager wd_manager; - std::set auto_form_set; - bool shortkey_occurred{ false }; - - struct menu_rep - { - core_window_t* taken_window{ nullptr }; - bool delay_restore{ false }; - native_window_type window{ nullptr }; - native_window_type owner{ nullptr }; - bool has_keyboard{ false }; - }menu; - }; - } -} - -#include - -#endif diff --git a/include/nana/gui/detail/window_manager.hpp b/include/nana/gui/detail/window_manager.hpp index 0807978d..474cd2ea 100644 --- a/include/nana/gui/detail/window_manager.hpp +++ b/include/nana/gui/detail/window_manager.hpp @@ -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-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -83,12 +83,6 @@ namespace detail core_window_t* create_root(core_window_t*, bool nested, rectangle, const appearance&, widget*); core_window_t* create_widget(core_window_t*, const rectangle&, bool is_lite, widget*); -#ifndef WIDGET_FRAME_DEPRECATED - core_window_t* create_frame(core_window_t*, const rectangle&, widget*); - - bool insert_frame(core_window_t* frame, native_window); - bool insert_frame(core_window_t* frame, core_window_t*); -#endif void close(core_window_t*); //destroy @@ -122,6 +116,7 @@ namespace detail void refresh_tree(core_window_t*); void do_lazy_refresh(core_window_t*, bool force_copy_to_screen, bool refresh_tree = false); + void map_requester(core_window_t*); bool get_graphics(core_window_t*, nana::paint::graphics&); bool get_visual_rectangle(core_window_t*, nana::rectangle&); diff --git a/include/nana/gui/programming_interface.hpp b/include/nana/gui/programming_interface.hpp index 56c3ec7d..2fdd3c14 100644 --- a/include/nana/gui/programming_interface.hpp +++ b/include/nana/gui/programming_interface.hpp @@ -1,7 +1,7 @@ /* * Nana GUI Programming Interface Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -95,9 +95,7 @@ namespace API window create_window(window, bool nested, const rectangle&, const appearance&, widget* attached); window create_widget(window, const rectangle&, widget* attached); window create_lite_widget(window, const rectangle&, widget* attached); -#ifndef WIDGET_FRAME_DEPRECATED - window create_frame(window, const rectangle&, widget* attached); -#endif + paint::graphics* window_graphics(window); void delay_restore(bool); @@ -234,11 +232,6 @@ namespace API void fullscreen(window, bool); bool enabled_double_click(window, bool); -#ifndef WIDGET_FRAME_DEPRECATED - bool insert_frame(window frame, native_window_type); - native_window_type frame_container(window frame); - native_window_type frame_element(window frame, unsigned index); -#endif void close_window(window); void show_window(window, bool show); ///< Sets a window visible state. void restore_window(window); diff --git a/include/nana/gui/widgets/frame.hpp b/include/nana/gui/widgets/frame.hpp deleted file mode 100644 index 82a53d95..00000000 --- a/include/nana/gui/widgets/frame.hpp +++ /dev/null @@ -1,56 +0,0 @@ -/** - * A Frame Implementation - * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com) - * - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at - * http://www.boost.org/LICENSE_1_0.txt) - * - * @file: nana/gui/widgets/frame.hpp - * - * @brief A frame provides a way to contain the platform window in a stdex GUI Window - */ - -#ifndef NANA_GUI_WIDGET_FRAME_HPP -#define NANA_GUI_WIDGET_FRAME_HPP - -#include "widget.hpp" - -#ifndef WIDGET_FRAME_DEPRECATED -namespace nana -{ - /** - \brief Container for system native windows. Provides an approach to - display a control that is not written with Nana.GUI in a Nana.GUI window. - - Notes: - - 1. nana::native_window_type is a type of system handle of windows. - 2. all the children windows of a nana::frame is topmost to Nana.GUI windows. - 3. a simple example. Displaying a Windows Edit Control. - - nana::frame frame(parent, 0, 0 200, 100); - HWND frame_handle = reinterpret_cast(frame.container()); - HWND edit = ::CreateWindowExW(WS_EX_CLIENTEDGE, L"EDIT", L"Test", - WS_CHILD | WS_VISIBLE | WS_BORDER, 0, 0, 200, 100, - frame_handle, 0, ::GetModuleHandle(0), 0); - if(edit) - frame.insert(edit); - - */ - class frame: public widget_object - { - typedef widget_object base_type; - public: - frame(); - frame(window, bool visible); - frame(window, const rectangle& = rectangle(), bool visible = true); - bool insert(native_window_type); ///< Inserts a platform native window. - native_window_type element(unsigned index); ///< Returns the child window through index. - - native_window_type container() const; ///< Returns the frame container native window handle. - }; -}//end namespace nana -#endif -#endif diff --git a/include/nana/gui/widgets/widget.hpp b/include/nana/gui/widgets/widget.hpp index 7448eea9..fe8fe543 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-2018 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -499,72 +499,6 @@ namespace nana std::unique_ptr scheme_; };//end class widget_object -#ifndef WIDGET_FRAME_DEPRECATED - /// Base class of all the classes defined as a frame window. \see nana::frame - template - class widget_object: public widget{}; - - /// Especialization. Base class of all the classes defined as a frame window. \see nana::frame - template - class widget_object: public detail::widget_base - { - protected: - typedef int drawer_trigger_t; - public: - using scheme_type = Scheme; - using event_type = Events; - - widget_object() - : events_{ std::make_shared() }, scheme_{ API::dev::make_scheme() } - {} - - ~widget_object() - { - API::close_window(handle()); - } - - event_type& events() const - { - return *events_; - } - - bool create(window parent_wd, bool visible) ///< Creates a no-size (zero-size) widget. in a widget/root window specified by parent_wd. - { - return create(parent_wd, rectangle(), visible); - } - /// Creates in a widget/root window specified by parent_wd. - bool create(window parent_wd, const rectangle& r = rectangle(), bool visible = true) - { - if(parent_wd && this->empty()) - { - handle_ = API::dev::create_frame(parent_wd, r, this); - API::dev::set_events(handle_, events_); - API::dev::set_scheme(handle_, scheme_.get()); - API::show_window(handle_, visible); - this->_m_complete_creation(); - } - return (this->empty() == false); - } - - scheme_type& scheme() const - { - return *scheme_; - } - private: - virtual drawer_trigger* get_drawer_trigger() - { - return nullptr; - } - - general_events& _m_get_general_events() const override - { - return *events_; - } - private: - std::shared_ptr events_; - std::unique_ptr scheme_; - };//end class widget_object -#endif }//end namespace nana #include diff --git a/source/gui/detail/basic_window.cpp b/source/gui/detail/basic_window.cpp index e0f6b02d..a52b2215 100644 --- a/source/gui/detail/basic_window.cpp +++ b/source/gui/detail/basic_window.cpp @@ -1,7 +1,7 @@ /* * A Basic Window Widget Definition * Nana C++ Library(http://www.nanapro.org) -* Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) +* Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -216,40 +216,14 @@ namespace nana basic_window::other_tag::other_tag(category::flags categ) : category(categ), active_window(nullptr), upd_state(update_state::none) { -#ifndef WIDGET_FRAME_DEPRECATED - switch(categ) - { - case category::flags::root: - attribute.root = new attr_root_tag; - break; - case category::flags::frame: - attribute.frame = new attr_frame_tag; - break; - default: - attribute.root = nullptr; - } -#else if (category::flags::root == categ) attribute.root = new attr_root_tag; else attribute.root = nullptr; -#endif } basic_window::other_tag::~other_tag() { -#ifndef WIDGET_FRAME_DEPRECATED - switch(category) - { - case category::flags::root: - delete attribute.root; - break; - case category::flags::frame: - delete attribute.frame; - break; - default: break; - } -#endif if (category::flags::root == category) delete attribute.root; } @@ -290,14 +264,6 @@ namespace nana } } -#ifndef WIDGET_FRAME_DEPRECATED - void basic_window::frame_window(native_window_type wd) - { - if(category::flags::frame == this->other.category) - other.attribute.frame->container = wd; - } -#endif - bool basic_window::is_ancestor_of(const basic_window* wd) const { while (wd) @@ -424,6 +390,7 @@ namespace nana flags.ignore_menubar_focus = false; flags.ignore_mouse_focus = false; flags.space_click_enabled = false; + flags.ignore_child_mapping = false; visible = false; diff --git a/source/gui/detail/bedrock_pi.cpp b/source/gui/detail/bedrock_pi.cpp index 221d04c8..65d644ec 100644 --- a/source/gui/detail/bedrock_pi.cpp +++ b/source/gui/detail/bedrock_pi.cpp @@ -1,7 +1,7 @@ /* * A Bedrock Platform-Independent Implementation * Nana C++ Library(http://www.nanapro.org) -* Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) +* Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -11,7 +11,7 @@ */ #include "../../detail/platform_spec_selector.hpp" -#include +#include "bedrock_types.hpp" #include #include #include @@ -94,6 +94,12 @@ namespace nana }; + bedrock::core_window_t* bedrock::focus() + { + auto wd = wd_manager().root(native_interface::get_focus_window()); + return (wd ? wd->other.attribute.root->focus : nullptr); + } + events_operation& bedrock::evt_operation() { return pi_data_->evt_operation; @@ -164,17 +170,10 @@ namespace nana caret_wd->annex.caret_ptr->visible(false); } - if (!exposed) + if ((!exposed) && (category::flags::root != wd->other.category)) { - if (category::flags::root != wd->other.category) - { - //find an ancestor until it is not a lite_widget - wd = wd->seek_non_lite_widget_ancestor(); - } -#ifndef WIDGET_FRAME_DEPRECATED - else if (category::flags::frame == wd->other.category) - wd = wd_manager().find_window(wd->root, wd->pos_root.x, wd->pos_root.y); -#endif + //find an ancestor until it is not a lite_widget + wd = wd->seek_non_lite_widget_ancestor(); } wd_manager().refresh_tree(wd); @@ -609,5 +608,93 @@ namespace nana throw std::runtime_error("Invalid event code"); } } + + void bedrock::thread_context_destroy(core_window_t * wd) + { + auto ctx = get_thread_context(0); + if(ctx && ctx->event_window == wd) + ctx->event_window = nullptr; + } + + void bedrock::thread_context_lazy_refresh() + { + auto ctx = get_thread_context(0); + if(ctx && ctx->event_window) + ctx->event_window->other.upd_state = core_window_t::update_state::refreshed; + } + + bool bedrock::emit(event_code evt_code, core_window_t* wd, const ::nana::event_arg& arg, bool ask_update, thread_context* thrd, const bool bForce__EmitInternal) + { + if(wd_manager().available(wd) == false) + return false; + + core_window_t * prev_wd = nullptr; + if(thrd) + { + prev_wd = thrd->event_window; + thrd->event_window = wd; + _m_event_filter(evt_code, wd, thrd); + } + + using update_state = basic_window::update_state; + + if (update_state::none == wd->other.upd_state) + wd->other.upd_state = update_state::lazy; + + auto ignore_mapping_value = wd->flags.ignore_child_mapping; + wd->flags.ignore_child_mapping = true; + + _m_emit_core(evt_code, wd, false, arg, bForce__EmitInternal); + + wd->flags.ignore_child_mapping = ignore_mapping_value; + + bool good_wd = false; + if(wd_manager().available(wd)) + { + //A child of wd may not be drawn if it was out of wd's range before wd resized, + //so refresh all children of wd when a resized occurs. + if(ask_update || (event_code::resized == evt_code) || (update_state::refreshed == wd->other.upd_state)) + { + wd_manager().do_lazy_refresh(wd, false, (event_code::resized == evt_code)); + } + else + { + wd_manager().map_requester(wd); + wd->other.upd_state = update_state::none; + } + + good_wd = true; + } + + + if(thrd) thrd->event_window = prev_wd; + return good_wd; + } + + void bedrock::_m_event_filter(event_code event_id, core_window_t * wd, thread_context * thrd) + { + auto not_state_cur = (wd->root_widget->other.attribute.root->state_cursor == nana::cursor::arrow); + + switch(event_id) + { + case event_code::mouse_enter: + if (not_state_cur) + set_cursor(wd, wd->predef_cursor, thrd); + break; + case event_code::mouse_leave: + if (not_state_cur && (wd->predef_cursor != cursor::arrow)) + set_cursor(wd, nana::cursor::arrow, thrd); + break; + case event_code::destroy: + if (wd->root_widget->other.attribute.root->state_cursor_window == wd) + undefine_state_cursor(wd, thrd); + + if(wd == thrd->cursor.window) + set_cursor(wd, cursor::arrow, thrd); + break; + default: + break; + } + } }//end namespace detail }//end namespace nana diff --git a/source/gui/detail/bedrock_posix.cpp b/source/gui/detail/bedrock_posix.cpp index c28497f6..07045626 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-2018 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -12,7 +12,6 @@ #include "../../detail/platform_spec_selector.hpp" #if defined(NANA_POSIX) && defined(NANA_X11) -#include #include #include #include @@ -22,6 +21,8 @@ #include #include +#include "bedrock_types.hpp" + namespace nana { namespace detail @@ -52,6 +53,7 @@ namespace detail }; #pragma pack() +#if 0 //deprecated struct bedrock::thread_context { unsigned event_pump_ref_count{0}; @@ -83,6 +85,7 @@ namespace detail cursor.handle = 0; } }; +#endif struct bedrock::private_impl { @@ -247,12 +250,14 @@ namespace detail return bedrock_object; } +#if 0 //deprecated bedrock::core_window_t* bedrock::focus() { core_window_t* wd = wd_manager().root(native_interface::get_focus_window()); return (wd ? wd->other.attribute.root->focus : 0); } - +#endif + void bedrock::get_key_state(arg_keyboard& arg) { XKeyEvent xkey; @@ -289,6 +294,7 @@ namespace detail //No implementation for Linux } +#if 0 //deprecated bool bedrock::emit(event_code evt_code, core_window_t* wd, const ::nana::event_arg& arg, bool ask_update, thread_context* thrd, const bool bForce__EmitInternal) { if(wd_manager().available(wd) == false) @@ -304,11 +310,16 @@ namespace detail using update_state = basic_window::update_state; - if(wd->other.upd_state == update_state::none) + if (update_state::none == wd->other.upd_state) wd->other.upd_state = update_state::lazy; + auto ignore_mapping_value = wd->flags.ignore_child_mapping; + wd->flags.ignore_child_mapping = true; + _m_emit_core(evt_code, wd, false, arg, bForce__EmitInternal); + wd->flags.ignore_child_mapping = ignore_mapping_value; + bool good_wd = false; if(wd_manager().available(wd)) { @@ -319,7 +330,10 @@ namespace detail wd_manager().do_lazy_refresh(wd, false, (event_code::resized == evt_code)); } else + { + wd_manager().map_requester(wd); wd->other.upd_state = update_state::none; + } good_wd = true; } @@ -328,6 +342,7 @@ namespace detail if(thrd) thrd->event_window = prev_wd; return good_wd; } +#endif void assign_arg(arg_mouse& arg, basic_window* wd, unsigned msg, const XEvent& evt) { @@ -1346,6 +1361,7 @@ namespace detail }//end bedrock::event_loop +#if 0 //deprecated void bedrock::thread_context_destroy(core_window_t * wd) { bedrock::thread_context * thr = get_thread_context(0); @@ -1359,6 +1375,7 @@ namespace detail if(thrd && thrd->event_window) thrd->event_window->other.upd_state = core_window_t::update_state::refreshed; } +#endif //Dynamically set a cursor for a window void bedrock::set_cursor(core_window_t* wd, nana::cursor cur, thread_context* thrd) @@ -1430,6 +1447,7 @@ namespace detail set_cursor(rev_wd, rev_wd->predef_cursor, thrd); } +#if 0 //deprecated void bedrock::_m_event_filter(event_code event_id, core_window_t * wd, thread_context * thrd) { auto not_state_cur = (wd->root_widget->other.attribute.root->state_cursor == nana::cursor::arrow); @@ -1455,6 +1473,7 @@ namespace detail break; } } +#endif }//end namespace detail }//end namespace nana #endif //NANA_POSIX && NANA_X11 diff --git a/source/gui/detail/bedrock_types.hpp b/source/gui/detail/bedrock_types.hpp new file mode 100644 index 00000000..f3fb55bc --- /dev/null +++ b/source/gui/detail/bedrock_types.hpp @@ -0,0 +1,101 @@ +#ifndef NANA_GUI_DETAIL_BEDROCK_TYPES_INCLUDED +#define NANA_GUI_DETAIL_BEDROCK_TYPES_INCLUDED + +#include + +#include +#include +#include +#include +#include + +namespace nana +{ + namespace detail + { + struct bedrock::pi_data + { + color_schemes scheme; + events_operation evt_operation; + window_manager wd_manager; + std::set auto_form_set; + bool shortkey_occurred{ false }; + + struct menu_rep + { + core_window_t* taken_window{ nullptr }; + bool delay_restore{ false }; + native_window_type window{ nullptr }; + native_window_type owner{ nullptr }; + bool has_keyboard{ false }; + }menu; + }; + + +#ifdef NANA_WINDOWS + struct bedrock::thread_context + { + unsigned event_pump_ref_count{0}; + int window_count{0}; //The number of windows + core_window_t* event_window{nullptr}; + + struct platform_detail_tag + { + wchar_t keychar; + }platform; + + struct cursor_tag + { + core_window_t * window; + native_window_type native_handle; + nana::cursor predef_cursor; + HCURSOR handle; + }cursor; + + thread_context() + { + cursor.window = nullptr; + cursor.native_handle = nullptr; + cursor.predef_cursor = nana::cursor::arrow; + cursor.handle = nullptr; + } + }; +#else + struct bedrock::thread_context + { + unsigned event_pump_ref_count{0}; + + int window_count{0}; //The number of windows + core_window_t* event_window{nullptr}; + bool is_alt_pressed{false}; + bool is_ctrl_pressed{false}; + + struct platform_detail_tag + { + native_window_type motion_window; + nana::point motion_pointer_pos; + }platform; + + struct cursor_tag + { + core_window_t * window; + native_window_type native_handle; + nana::cursor predef_cursor; + Cursor handle; + }cursor; + + thread_context() + { + cursor.window = nullptr; + cursor.native_handle = nullptr; + cursor.predef_cursor = nana::cursor::arrow; + cursor.handle = 0; + } + }; +#endif + } +} + +#include + +#endif \ No newline at end of file diff --git a/source/gui/detail/bedrock_windows.cpp b/source/gui/detail/bedrock_windows.cpp index 674fe475..5c1f9701 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-2018 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -14,8 +14,8 @@ #include "../../detail/platform_spec_selector.hpp" #if defined(NANA_WINDOWS) -#include -#include +//#include //deprecated +#include "bedrock_types.hpp" #include #include #include @@ -36,6 +36,8 @@ #define WM_MOUSEHWHEEL 0x020E #endif +#include "bedrock_types.hpp" + typedef void (CALLBACK *win_event_proc_t)(HWINEVENTHOOK hWinEventHook, DWORD event, HWND hwnd, LONG idObject, LONG idChild, DWORD dwEventThread, DWORD dwmsEventTime); namespace nana @@ -135,6 +137,7 @@ namespace detail }; #pragma pack() +#if 0 //deprecated struct bedrock::thread_context { unsigned event_pump_ref_count{0}; @@ -162,6 +165,7 @@ namespace detail cursor.handle = nullptr; } }; +#endif struct bedrock::private_impl { @@ -1594,11 +1598,13 @@ namespace detail return ::DefWindowProc(root_window, message, wParam, lParam); } +#if 0 //deprecated auto bedrock::focus() ->core_window_t* { core_window_t* wd = wd_manager().root(native_interface::get_focus_window()); return (wd ? wd->other.attribute.root->focus : nullptr); } +#endif void bedrock::get_key_state(arg_keyboard& kb) { @@ -1677,6 +1683,7 @@ namespace detail } } +#if 0 //deprecated bool bedrock::emit(event_code evt_code, core_window_t* wd, const ::nana::event_arg& arg, bool ask_update, thread_context* thrd, const bool bForce__EmitInternal) { if (wd_manager().available(wd) == false) @@ -1695,16 +1702,27 @@ namespace detail if (update_state::none == wd->other.upd_state) wd->other.upd_state = update_state::lazy; + auto ignore_mapping_value = wd->flags.ignore_child_mapping; + wd->flags.ignore_child_mapping = true; + _m_emit_core(evt_code, wd, false, arg, bForce__EmitInternal); + wd->flags.ignore_child_mapping = ignore_mapping_value; + bool good_wd = false; if (wd_manager().available(wd)) { - //Ignore ask_update if update state is refreshed. - if (ask_update || (update_state::refreshed == wd->other.upd_state)) - wd_manager().do_lazy_refresh(wd, false); + //A child of wd may not be drawn if it was out of wd's range before wd resized, + //so refresh all children of wd when a resized occurs. + if(ask_update || (event_code::resized == evt_code) || (update_state::refreshed == wd->other.upd_state)) + { + wd_manager().do_lazy_refresh(wd, false, (event_code::resized == evt_code)); + } else + { + wd_manager().map_requester(wd); wd->other.upd_state = update_state::none; + } good_wd = true; } @@ -1712,6 +1730,7 @@ namespace detail if (thrd) thrd->event_window = prev_event_wd; return good_wd; } +#endif const wchar_t* translate(cursor id) { @@ -1740,7 +1759,7 @@ namespace detail } return name; } - +#if 0 //deprecated void bedrock::thread_context_destroy(core_window_t * wd) { auto * thr = get_thread_context(0); @@ -1754,7 +1773,7 @@ namespace detail if (thrd && thrd->event_window) thrd->event_window->other.upd_state = core_window_t::update_state::refreshed; } - +#endif //Dynamically set a cursor for a window void bedrock::set_cursor(core_window_t* wd, nana::cursor cur, thread_context* thrd) { @@ -1847,7 +1866,7 @@ namespace detail ::ShowCursor(FALSE); ::SetCursor(rev_handle); } - +#if 0 //deprecated void bedrock::_m_event_filter(event_code event_id, core_window_t * wd, thread_context * thrd) { auto not_state_cur = (wd->root_widget->other.attribute.root->state_cursor == nana::cursor::arrow); @@ -1873,6 +1892,7 @@ namespace detail break; } } +#endif }//end namespace detail }//end namespace nana #endif //NANA_WINDOWS diff --git a/source/gui/detail/window_layout.cpp b/source/gui/detail/window_layout.cpp index 19ec8133..12b5f86f 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-2018 Jinhao(cnjinhao@hotmail.com) +* Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -66,20 +66,9 @@ namespace nana nana::point p_src; for (auto & el : blocks) { -#ifndef WIDGET_FRAME_DEPRECATED - if (category::flags::frame == el.window->other.category) - { - native_window_type container = el.window->other.attribute.frame->container; - native_interface::refresh_window(container); - graph.bitblt(el.r, container); - } - else -#endif - { - p_src.x = el.r.x - el.window->pos_root.x; - p_src.y = el.r.y - el.window->pos_root.y; - graph.bitblt(el.r, (el.window->drawer.graphics), p_src); - } + p_src.x = el.r.x - el.window->pos_root.x; + p_src.y = el.r.y - el.window->pos_root.y; + graph.bitblt(el.r, (el.window->drawer.graphics), p_src); _m_paste_children(el.window, false, req_refresh_children, el.r, graph, nana::point{}); } diff --git a/source/gui/detail/window_manager.cpp b/source/gui/detail/window_manager.cpp index c9184053..78ea0927 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-2018 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -513,12 +513,7 @@ namespace detail if (owner->flags.destroying) throw std::runtime_error("the specified owner is destoryed"); -#ifndef WIDGET_FRAME_DEPRECATED - native = (category::flags::frame == owner->other.category ? - owner->other.attribute.frame->container : owner->root_widget->root); -#else native = owner->root_widget->root; -#endif r.x += owner->pos_root.x; r.y += owner->pos_root.y; } @@ -550,11 +545,6 @@ namespace detail wd->bind_native_window(result.native_handle, result.width, result.height, result.extra_width, result.extra_height, value->root_graph); impl_->wd_register.insert(wd); -#ifndef WIDGET_FRAME_DEPRECATED - if (owner && (category::flags::frame == owner->other.category)) - insert_frame(owner, wd); -#endif - bedrock::inc_window(wd->thread_id); this->icon(wd, impl_->default_icon_small, impl_->default_icon_big); return wd; @@ -562,56 +552,6 @@ namespace detail return nullptr; } -#ifndef WIDGET_FRAME_DEPRECATED - window_manager::core_window_t* window_manager::create_frame(core_window_t* parent, const rectangle& r, widget* wdg) - { - //Thread-Safe Required! - std::lock_guard lock(mutex_); - - if (impl_->wd_register.available(parent) == false) return nullptr; - - core_window_t * wd = new core_window_t(parent, widget_notifier_interface::get_notifier(wdg), r, (category::frame_tag**)nullptr); - wd->frame_window(native_interface::create_child_window(parent->root, rectangle(wd->pos_root.x, wd->pos_root.y, r.width, r.height))); - impl_->wd_register.insert(wd, wd->thread_id); - - //Insert the frame_widget into its root frames container. - wd->root_widget->other.attribute.root->frames.push_back(wd); - return (wd); - } - - - bool window_manager::insert_frame(core_window_t* frame, native_window wd) - { - if(frame) - { - //Thread-Safe Required! - std::lock_guard lock(mutex_); - if(category::flags::frame == frame->other.category) - frame->other.attribute.frame->attach.push_back(wd); - return true; - } - return false; - } - - bool window_manager::insert_frame(core_window_t* frame, core_window_t* wd) - { - if(frame) - { - //Thread-Safe Required! - std::lock_guard lock(mutex_); - if(category::flags::frame == frame->other.category) - { - if (impl_->wd_register.available(wd) && (category::flags::root == wd->other.category) && wd->root != frame->root) - { - frame->other.attribute.frame->attach.push_back(wd->root); - return true; - } - } - } - return false; - } -#endif - window_manager::core_window_t* window_manager::create_widget(core_window_t* parent, const rectangle& r, bool is_lite, widget* wdg) { //Thread-Safe Required! @@ -706,11 +646,7 @@ namespace detail std::lock_guard lock(mutex_); if (impl_->wd_register.available(wd) == false) return; -#ifndef WIDGET_FRAME_DEPRECATED - if((category::flags::root == wd->other.category) || (category::flags::frame != wd->other.category)) -#else if (category::flags::root == wd->other.category) -#endif { impl_->misc_register.erase(wd->root); impl_->wd_register.remove(wd); @@ -749,20 +685,7 @@ namespace detail if(visible != wd->visible) { -#ifndef WIDGET_FRAME_DEPRECATED - native_window_type nv = nullptr; - switch(wd->other.category) - { - case category::flags::root: - nv = wd->root; break; - case category::flags::frame: - nv = wd->other.attribute.frame->container; break; - default: //category::widget_tag, category::lite_widget_tag - break; - } -#else auto nv = (category::flags::root == wd->other.category ? wd->root : nullptr); -#endif if(visible && wd->effect.bground) window_layer::make_bground(wd); @@ -1012,22 +935,11 @@ namespace detail return false; } } -#ifndef WIDGET_FRAME_DEPRECATED - else if(category::flags::frame == wd->other.category) - { - native_interface::window_size(wd->other.attribute.frame->container, sz); - for(auto natwd : wd->other.attribute.frame->attach) - native_interface::window_size(natwd, sz); - } -#endif - else + else if(wd->effect.bground && wd->parent) { //update the bground buffer of glass window. - if(wd->effect.bground && wd->parent) - { - wd->other.glass_buffer.make(sz); - window_layer::make_bground(wd); - } + wd->other.glass_buffer.make(sz); + window_layer::make_bground(wd); } } @@ -1072,8 +984,19 @@ namespace detail auto parent = wd->parent; while (parent) { - if (parent->flags.refreshing) + if(parent->flags.ignore_child_mapping || parent->flags.refreshing) + { + auto top = parent; + while(parent->parent) + { + parent = parent->parent; + if(parent->flags.ignore_child_mapping || parent->flags.refreshing) + top = parent; + } + + top->other.mapping_requester.push_back(wd); return; + } parent = parent->parent; } @@ -1168,7 +1091,24 @@ namespace detail window_layer::paint(wd, paint_operation::try_refresh, refresh_tree); //only refreshing if it has an invisible parent } wd->other.upd_state = core_window_t::update_state::none; - return; + wd->other.mapping_requester.clear(); + } + + void window_manager::map_requester(core_window_t* wd) + { + //Thread-Safe Required! + std::lock_guard lock(mutex_); + + if (false == impl_->wd_register.available(wd)) + return; + + if (wd->visible_parents()) + { + for(auto requestor : wd->other.mapping_requester) + this->map(requestor, true); + } + + wd->other.mapping_requester.clear(); } //get_graphics @@ -1758,17 +1698,6 @@ namespace detail } } -#ifndef WIDGET_FRAME_DEPRECATED - if (category::flags::frame == wd->other.category) - { - //remove the frame handle from the WM frames manager. - utl::erase(root_attr->frames, wd); - - if (established) - pa_root_attr->frames.push_back(wd); - } -#endif - if (established) { wd->parent = for_new; @@ -1857,18 +1786,6 @@ namespace detail wd->drawer.detached(); wd->widget_notifier->destroy(); -#ifndef WIDGET_FRAME_DEPRECATED - if(category::flags::frame == wd->other.category) - { - //The frame widget does not have an owner, and close their element windows without activating owner. - //close the frame container window, it's a native window. - for(auto i : wd->other.attribute.frame->attach) - native_interface::close_window(i); - - native_interface::close_window(wd->other.attribute.frame->container); - } -#endif - if(wd->other.category != category::flags::root) //Not a root window impl_->wd_register.remove(wd); @@ -1881,18 +1798,9 @@ namespace detail if(category::flags::root != wd->other.category) //A root widget always starts at (0, 0) and its childs are not to be changed { wd->pos_root += delta; -#ifndef WIDGET_FRAME_DEPRECATED - if (category::flags::frame != wd->other.category) - { - if (wd->annex.caret_ptr && wd->annex.caret_ptr->visible()) - wd->annex.caret_ptr->update(); - } - else - native_interface::move_window(wd->other.attribute.frame->container, wd->pos_root.x, wd->pos_root.y); -#else + if (wd->annex.caret_ptr && wd->annex.caret_ptr->visible()) wd->annex.caret_ptr->update(); -#endif if (wd->displayed() && wd->effect.bground) window_layer::make_bground(wd); diff --git a/source/gui/programming_interface.cpp b/source/gui/programming_interface.cpp index 9791d2fd..3218069d 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-2018 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -303,13 +303,6 @@ namespace API return reinterpret_cast(restrict::wd_manager().create_widget(reinterpret_cast(parent), r, true, wdg)); } -#ifndef WIDGET_FRAME_DEPRECATED - window create_frame(window parent, const rectangle& r, widget* wdg) - { - return reinterpret_cast(restrict::wd_manager().create_frame(reinterpret_cast(parent), r, wdg)); - } -#endif - paint::graphics* window_graphics(window wd) { internal_scope_guard isg; @@ -600,34 +593,6 @@ namespace API reinterpret_cast(wd)->flags.fullscreen = v; } -#ifndef WIDGET_FRAME_DEPRECATED - bool insert_frame(window frame, native_window_type native_window) - { - return restrict::wd_manager().insert_frame(reinterpret_cast(frame), native_window); - } - - native_window_type frame_container(window frame) - { - auto frm = reinterpret_cast(frame); - internal_scope_guard lock; - if (restrict::wd_manager().available(frm) && (frm->other.category == category::flags::frame)) - return frm->other.attribute.frame->container; - return nullptr; - } - - native_window_type frame_element(window frame, unsigned index) - { - auto frm = reinterpret_cast(frame); - internal_scope_guard lock; - if (restrict::wd_manager().available(frm) && (frm->other.category == category::flags::frame)) - { - if (index < frm->other.attribute.frame->attach.size()) - return frm->other.attribute.frame->attach.at(index); - } - return nullptr; - } -#endif - void close_window(window wd) { restrict::wd_manager().close(reinterpret_cast(wd)); diff --git a/source/gui/widgets/frame.cpp b/source/gui/widgets/frame.cpp deleted file mode 100644 index 7b9e7af2..00000000 --- a/source/gui/widgets/frame.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * A Frame Implementation - * Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com) - * - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at - * http://www.boost.org/LICENSE_1_0.txt) - * - * @file: nana/gui/widgets/frame.cpp - * - * A frame provides a way to contain the platform window in a stdex GUI Window - */ - -#include - -#ifndef WIDGET_FRAME_DEPRECATED - -namespace nana -{ - //class frame:: public widget_object - frame::frame(){} - - frame::frame(window wd, bool visible) - { - create(wd, rectangle(), visible); - } - - frame::frame(window wd, const nana::rectangle& r, bool visible) - { - create(wd, r, visible); - } - - bool frame::insert(native_window_type wd) - { - return API::insert_frame(handle(), wd); - } - - native_window_type frame::element(unsigned index) - { - return API::frame_element(handle(), index); - } - - native_window_type frame::container() const - { - return API::frame_container(handle()); - } - //end class frame -}//end namespace nana - -#endif - From 240fb82ab3852d33fba1e7376dd69660e743d229 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Wed, 20 Feb 2019 23:04:03 +0800 Subject: [PATCH 134/149] remove deprecated code --- include/nana/gui/dragdrop.hpp | 12 ++- source/gui/detail/bedrock_posix.cpp | 136 -------------------------- source/gui/detail/bedrock_windows.cpp | 129 ------------------------ 3 files changed, 8 insertions(+), 269 deletions(-) diff --git a/include/nana/gui/dragdrop.hpp b/include/nana/gui/dragdrop.hpp index a20c7bb8..1eeb9541 100644 --- a/include/nana/gui/dragdrop.hpp +++ b/include/nana/gui/dragdrop.hpp @@ -1,7 +1,7 @@ /** * Drag and Drop Implementation * Nana C++ Library(http://www.nanapro.org) -* Copyright(C) 2018 Jinhao(cnjinhao@hotmail.com) +* Copyright(C) 2018-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -43,7 +43,11 @@ namespace nana simple_dragdrop(window source); ~simple_dragdrop(); - /// Sets a condition that determines whether the drag&drop can start + /// Condition checker + /** + * Sets a condition checker that determines whether the drag-and-drop operation can start. If a condition checker is not set, it always start drag-and-drop operation. + * @param predicate_fn Unary predicate which returns #true# for starting drag-and-drop operation. + */ void condition(std::function predicate_fn); void make_drop(window target, std::function drop_fn); private: @@ -94,9 +98,9 @@ namespace nana dragdrop(window source); ~dragdrop(); - /// Condition of dragging + /// Condition checker /*** - * The preciate function is called when press mouse button on the source window, it returns true to indicate the start of dragging. If the predicate is not set, it always start to drag. + * Sets a condition checker that determines whether the drag-and-drop operation can start. If a condition checker is not set, it always start drag-and-drop operation. * @param predicate_fn A predicate function to be set. */ void condition(std::function predicate_fn); diff --git a/source/gui/detail/bedrock_posix.cpp b/source/gui/detail/bedrock_posix.cpp index 07045626..65e66701 100644 --- a/source/gui/detail/bedrock_posix.cpp +++ b/source/gui/detail/bedrock_posix.cpp @@ -53,40 +53,6 @@ namespace detail }; #pragma pack() -#if 0 //deprecated - struct bedrock::thread_context - { - unsigned event_pump_ref_count{0}; - - int window_count{0}; //The number of windows - core_window_t* event_window{nullptr}; - bool is_alt_pressed{false}; - bool is_ctrl_pressed{false}; - - struct platform_detail_tag - { - native_window_type motion_window; - nana::point motion_pointer_pos; - }platform; - - struct cursor_tag - { - core_window_t * window; - native_window_type native_handle; - nana::cursor predef_cursor; - Cursor handle; - }cursor; - - thread_context() - { - cursor.window = nullptr; - cursor.native_handle = nullptr; - cursor.predef_cursor = nana::cursor::arrow; - cursor.handle = 0; - } - }; -#endif - struct bedrock::private_impl { typedef std::map thr_context_container; @@ -249,14 +215,6 @@ namespace detail { return bedrock_object; } - -#if 0 //deprecated - bedrock::core_window_t* bedrock::focus() - { - core_window_t* wd = wd_manager().root(native_interface::get_focus_window()); - return (wd ? wd->other.attribute.root->focus : 0); - } -#endif void bedrock::get_key_state(arg_keyboard& arg) { @@ -294,56 +252,6 @@ namespace detail //No implementation for Linux } -#if 0 //deprecated - bool bedrock::emit(event_code evt_code, core_window_t* wd, const ::nana::event_arg& arg, bool ask_update, thread_context* thrd, const bool bForce__EmitInternal) - { - if(wd_manager().available(wd) == false) - return false; - - core_window_t * prev_wd = nullptr; - if(thrd) - { - prev_wd = thrd->event_window; - thrd->event_window = wd; - _m_event_filter(evt_code, wd, thrd); - } - - using update_state = basic_window::update_state; - - if (update_state::none == wd->other.upd_state) - wd->other.upd_state = update_state::lazy; - - auto ignore_mapping_value = wd->flags.ignore_child_mapping; - wd->flags.ignore_child_mapping = true; - - _m_emit_core(evt_code, wd, false, arg, bForce__EmitInternal); - - wd->flags.ignore_child_mapping = ignore_mapping_value; - - bool good_wd = false; - if(wd_manager().available(wd)) - { - //A child of wd may not be drawn if it was out of wd's range before wd resized, - //so refresh all children of wd when a resized occurs. - if(ask_update || (event_code::resized == evt_code) || (update_state::refreshed == wd->other.upd_state)) - { - wd_manager().do_lazy_refresh(wd, false, (event_code::resized == evt_code)); - } - else - { - wd_manager().map_requester(wd); - wd->other.upd_state = update_state::none; - } - - good_wd = true; - } - - - if(thrd) thrd->event_window = prev_wd; - return good_wd; - } -#endif - void assign_arg(arg_mouse& arg, basic_window* wd, unsigned msg, const XEvent& evt) { arg.window_handle = reinterpret_cast(wd); @@ -1361,22 +1269,6 @@ namespace detail }//end bedrock::event_loop -#if 0 //deprecated - void bedrock::thread_context_destroy(core_window_t * wd) - { - bedrock::thread_context * thr = get_thread_context(0); - if(thr && thr->event_window == wd) - thr->event_window = nullptr; - } - - void bedrock::thread_context_lazy_refresh() - { - thread_context* thrd = get_thread_context(0); - if(thrd && thrd->event_window) - thrd->event_window->other.upd_state = core_window_t::update_state::refreshed; - } -#endif - //Dynamically set a cursor for a window void bedrock::set_cursor(core_window_t* wd, nana::cursor cur, thread_context* thrd) { @@ -1446,34 +1338,6 @@ namespace detail if (rev_wd) set_cursor(rev_wd, rev_wd->predef_cursor, thrd); } - -#if 0 //deprecated - void bedrock::_m_event_filter(event_code event_id, core_window_t * wd, thread_context * thrd) - { - auto not_state_cur = (wd->root_widget->other.attribute.root->state_cursor == nana::cursor::arrow); - - switch(event_id) - { - case event_code::mouse_enter: - if (not_state_cur) - set_cursor(wd, wd->predef_cursor, thrd); - break; - case event_code::mouse_leave: - if (not_state_cur && (wd->predef_cursor != cursor::arrow)) - set_cursor(wd, nana::cursor::arrow, thrd); - break; - case event_code::destroy: - if (wd->root_widget->other.attribute.root->state_cursor_window == wd) - undefine_state_cursor(wd, thrd); - - if(wd == thrd->cursor.window) - set_cursor(wd, cursor::arrow, thrd); - break; - default: - break; - } - } -#endif }//end namespace detail }//end namespace nana #endif //NANA_POSIX && NANA_X11 diff --git a/source/gui/detail/bedrock_windows.cpp b/source/gui/detail/bedrock_windows.cpp index 5c1f9701..30a05fb5 100644 --- a/source/gui/detail/bedrock_windows.cpp +++ b/source/gui/detail/bedrock_windows.cpp @@ -14,7 +14,6 @@ #include "../../detail/platform_spec_selector.hpp" #if defined(NANA_WINDOWS) -//#include //deprecated #include "bedrock_types.hpp" #include #include @@ -137,36 +136,6 @@ namespace detail }; #pragma pack() -#if 0 //deprecated - struct bedrock::thread_context - { - unsigned event_pump_ref_count{0}; - int window_count{0}; //The number of windows - core_window_t* event_window{nullptr}; - - struct platform_detail_tag - { - wchar_t keychar; - }platform; - - struct cursor_tag - { - core_window_t * window; - native_window_type native_handle; - nana::cursor predef_cursor; - HCURSOR handle; - }cursor; - - thread_context() - { - cursor.window = nullptr; - cursor.native_handle = nullptr; - cursor.predef_cursor = nana::cursor::arrow; - cursor.handle = nullptr; - } - }; -#endif - struct bedrock::private_impl { typedef std::map thr_context_container; @@ -1598,14 +1567,6 @@ namespace detail return ::DefWindowProc(root_window, message, wParam, lParam); } -#if 0 //deprecated - auto bedrock::focus() ->core_window_t* - { - core_window_t* wd = wd_manager().root(native_interface::get_focus_window()); - return (wd ? wd->other.attribute.root->focus : nullptr); - } -#endif - void bedrock::get_key_state(arg_keyboard& kb) { kb.alt = (0 != (::GetKeyState(VK_MENU) & 0x80)); @@ -1683,55 +1644,6 @@ namespace detail } } -#if 0 //deprecated - bool bedrock::emit(event_code evt_code, core_window_t* wd, const ::nana::event_arg& arg, bool ask_update, thread_context* thrd, const bool bForce__EmitInternal) - { - if (wd_manager().available(wd) == false) - return false; - - basic_window* prev_event_wd = nullptr; - if (thrd) - { - prev_event_wd = thrd->event_window; - thrd->event_window = wd; - _m_event_filter(evt_code, wd, thrd); - } - - using update_state = basic_window::update_state; - - if (update_state::none == wd->other.upd_state) - wd->other.upd_state = update_state::lazy; - - auto ignore_mapping_value = wd->flags.ignore_child_mapping; - wd->flags.ignore_child_mapping = true; - - _m_emit_core(evt_code, wd, false, arg, bForce__EmitInternal); - - wd->flags.ignore_child_mapping = ignore_mapping_value; - - bool good_wd = false; - if (wd_manager().available(wd)) - { - //A child of wd may not be drawn if it was out of wd's range before wd resized, - //so refresh all children of wd when a resized occurs. - if(ask_update || (event_code::resized == evt_code) || (update_state::refreshed == wd->other.upd_state)) - { - wd_manager().do_lazy_refresh(wd, false, (event_code::resized == evt_code)); - } - else - { - wd_manager().map_requester(wd); - wd->other.upd_state = update_state::none; - } - - good_wd = true; - } - - if (thrd) thrd->event_window = prev_event_wd; - return good_wd; - } -#endif - const wchar_t* translate(cursor id) { const wchar_t* name = IDC_ARROW; @@ -1759,21 +1671,7 @@ namespace detail } return name; } -#if 0 //deprecated - void bedrock::thread_context_destroy(core_window_t * wd) - { - auto * thr = get_thread_context(0); - if (thr && thr->event_window == wd) - thr->event_window = nullptr; - } - void bedrock::thread_context_lazy_refresh() - { - auto* thrd = get_thread_context(0); - if (thrd && thrd->event_window) - thrd->event_window->other.upd_state = core_window_t::update_state::refreshed; - } -#endif //Dynamically set a cursor for a window void bedrock::set_cursor(core_window_t* wd, nana::cursor cur, thread_context* thrd) { @@ -1866,33 +1764,6 @@ namespace detail ::ShowCursor(FALSE); ::SetCursor(rev_handle); } -#if 0 //deprecated - void bedrock::_m_event_filter(event_code event_id, core_window_t * wd, thread_context * thrd) - { - auto not_state_cur = (wd->root_widget->other.attribute.root->state_cursor == nana::cursor::arrow); - - switch(event_id) - { - case event_code::mouse_enter: - if (not_state_cur) - set_cursor(wd, wd->predef_cursor, thrd); - break; - case event_code::mouse_leave: - if (not_state_cur && (wd->predef_cursor != cursor::arrow)) - set_cursor(wd, cursor::arrow, thrd); - break; - case event_code::destroy: - if (wd->root_widget->other.attribute.root->state_cursor_window == wd) - undefine_state_cursor(wd, thrd); - - if(wd == thrd->cursor.window) - set_cursor(wd, cursor::arrow, thrd); - break; - default: - break; - } - } -#endif }//end namespace detail }//end namespace nana #endif //NANA_WINDOWS From bf27549beba77d28e8439f9ff33a2bbbad981c4b Mon Sep 17 00:00:00 2001 From: Jinhao Date: Sat, 23 Feb 2019 00:52:21 +0800 Subject: [PATCH 135/149] fix nana filesystem --- include/nana/filesystem/filesystem.hpp | 5 ++--- source/filesystem/filesystem.cpp | 7 ++++++- source/gui/detail/color_schemes.cpp | 10 +++++----- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/include/nana/filesystem/filesystem.hpp b/include/nana/filesystem/filesystem.hpp index c4815c94..83668943 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-2018 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -434,8 +434,7 @@ namespace nana { namespace experimental { namespace filesystem { return s.type() == file_type::directory ;} bool is_directory(const path& p); - - //bool is_directory(const path& p, error_code& ec) noexcept; + bool is_directory(const path& p, std::error_code& ec) noexcept; inline bool is_regular_file(file_status s) noexcept { diff --git a/source/filesystem/filesystem.cpp b/source/filesystem/filesystem.cpp index a61e2f66..4ec85bf5 100644 --- a/source/filesystem/filesystem.cpp +++ b/source/filesystem/filesystem.cpp @@ -1,6 +1,6 @@ /* * A ISO C++ FileSystem Implementation - * Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -995,6 +995,11 @@ namespace nana { namespace experimental { namespace filesystem return (status(p).type() == file_type::directory); } + bool is_directory(const path& p, std::error_code& ec) noexcept + { + return (status(p, ec).type() == file_type::directory); + } + std::uintmax_t file_size(const path& p) { #if defined(NANA_WINDOWS) diff --git a/source/gui/detail/color_schemes.cpp b/source/gui/detail/color_schemes.cpp index c0555b6f..3bfea8db 100644 --- a/source/gui/detail/color_schemes.cpp +++ b/source/gui/detail/color_schemes.cpp @@ -1,7 +1,7 @@ /* * Color Schemes * Nana C++ Library(http://www.nanapro.org) -* Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) +* Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -80,14 +80,14 @@ namespace nana color color_proxy::get(const color& default_color) const { - if (color_->invisible()) - return default_color; - return *color_; + if (color_ && !color_->invisible()) + return *color_; + return default_color; } color_proxy::operator color() const { - return *color_; + return (color_ ? *color_ : color{}); } //end class color_proxy From a9f37e4ca4d89282ac400447d9d10dd7cd54d33c Mon Sep 17 00:00:00 2001 From: beru Date: Sun, 24 Feb 2019 06:02:24 +0900 Subject: [PATCH 136/149] Use .gitattributes file to specify line endings for Visual Studio solutions and projects files Set project property Windows SDK Version to default Set project property Multi-processor Compilation to Yes (/MP) --- .gitattributes | 6 ++++++ build/vc2017/nana.vcxproj | 5 ++++- 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..83fd45e3 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +*.sln text eol=crlf +*.vcxproj text eol=crlf +*.vcxproj.filters text eol=crlf +*.vcxproj.user text eol=crlf +*.bat text eol=crlf +Makefile text eolf=lf diff --git a/build/vc2017/nana.vcxproj b/build/vc2017/nana.vcxproj index 85ad8a03..7ae3d890 100644 --- a/build/vc2017/nana.vcxproj +++ b/build/vc2017/nana.vcxproj @@ -23,7 +23,6 @@ {42D0520F-EFA5-4831-84FE-2B9085301C5D} Win32Proj nana - 10.0.14393.0 @@ -102,6 +101,7 @@ Disabled WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) MultiThreadedDebug + true Windows @@ -115,6 +115,7 @@ Disabled _DEBUG;_LIB;%(PreprocessorDefinitions) MultiThreadedDebug + true Windows @@ -130,6 +131,7 @@ true WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) MultiThreaded + true Windows @@ -147,6 +149,7 @@ true NDEBUG;_LIB;%(PreprocessorDefinitions) MultiThreaded + true Windows From 46ad9fd9f30f340489ef967ea19dc15da094b456 Mon Sep 17 00:00:00 2001 From: beru Date: Sun, 24 Feb 2019 09:53:36 +0900 Subject: [PATCH 137/149] Fix ctrl + end key handling, pos.y member should be calculated with _m_coordinate_to_caret method. --- source/gui/widgets/skeletons/text_editor.cpp | 21 ++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/source/gui/widgets/skeletons/text_editor.cpp b/source/gui/widgets/skeletons/text_editor.cpp index 7fd06c4b..b3015adc 100644 --- a/source/gui/widgets/skeletons/text_editor.cpp +++ b/source/gui/widgets/skeletons/text_editor.cpp @@ -2394,6 +2394,7 @@ namespace nana { auto origin = impl_->cview->origin(); auto pos = points_.caret; auto coord = _m_caret_to_coordinate(points_.caret, false); + auto coord_org = coord; wchar_t key = arg.key; @@ -2405,7 +2406,6 @@ namespace nana { //The number of charecters in the line of caret auto const text_length = textbase().getline(points_.caret.y).size(); - switch (key) { case keyboard::os_arrow_left: if (select_.move_to_end && (select_.a != select_.b) && (!arg.shift)) @@ -2451,12 +2451,19 @@ namespace nana { pos.y = 0; break; case keyboard::os_end: - //move the caret to the end of the line - pos.x = static_cast(text_length); //move the caret to the end of the text if Ctrl is pressed - if (arg.ctrl) - pos.y = static_cast((line_count - 1) * line_px); + if (arg.ctrl) { + coord.y = static_cast((line_count - 1) * line_px); + //The number of charecters of the bottom line + auto const text_length = textbase().getline(std::max(0u, line_count - 1)).size(); + //move the caret to the end of the line + pos.x = static_cast(text_length); + } + else { + //move the caret to the end of the line + pos.x = static_cast(text_length); + } break; case keyboard::os_pageup: if (origin.y > 0) @@ -2476,10 +2483,12 @@ namespace nana { break; } - if (pos == points_.caret) + if (coord != coord_org) { + auto pos_x = pos.x; impl_->cview->move_origin(origin - impl_->cview->origin()); pos = _m_coordinate_to_caret(coord, false); + pos.x = pos_x; } if (pos != points_.caret) { From b286ced6baf2461a713b6c2605990ac2fa044172 Mon Sep 17 00:00:00 2001 From: beru Date: Sun, 24 Feb 2019 10:29:34 +0900 Subject: [PATCH 138/149] fix compile error --- source/gui/widgets/skeletons/text_editor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/gui/widgets/skeletons/text_editor.cpp b/source/gui/widgets/skeletons/text_editor.cpp index b3015adc..b0c4c800 100644 --- a/source/gui/widgets/skeletons/text_editor.cpp +++ b/source/gui/widgets/skeletons/text_editor.cpp @@ -2456,7 +2456,7 @@ namespace nana { if (arg.ctrl) { coord.y = static_cast((line_count - 1) * line_px); //The number of charecters of the bottom line - auto const text_length = textbase().getline(std::max(0u, line_count - 1)).size(); + auto const text_length = textbase().getline(std::max(0, line_count - 1)).size(); //move the caret to the end of the line pos.x = static_cast(text_length); } From 29cee29f5f63483cdb377b98b46e77cfa0ba42bc Mon Sep 17 00:00:00 2001 From: Jinhao Date: Tue, 26 Feb 2019 04:24:40 +0800 Subject: [PATCH 139/149] refactor folderbox and filebox --- include/nana/gui/filebox.hpp | 19 ++- source/gui/filebox.cpp | 286 ++++++++++++++++++-------------- source/gui/msgbox.cpp | 5 +- source/internationalization.cpp | 3 +- 4 files changed, 180 insertions(+), 133 deletions(-) diff --git a/include/nana/gui/filebox.hpp b/include/nana/gui/filebox.hpp index 61ebdfa2..b9c5bc84 100644 --- a/include/nana/gui/filebox.hpp +++ b/include/nana/gui/filebox.hpp @@ -16,7 +16,6 @@ #define NANA_GUI_FILEBOX_HPP #include #include -#include #include #include @@ -30,6 +29,7 @@ namespace nana filebox& operator=(filebox&&) = delete; public: using filters = std::vector>; + using path_type = std::filesystem::path; explicit filebox(bool is_open_mode); filebox(window owner, bool is_open_mode); @@ -69,16 +69,14 @@ namespace nana const ::std::string& path() const; - ::std::string file() const; - - const ::std::vector<::std::string>& files() const; + void allow_multi_select(bool allow); /// Display the filebox dialog - bool show() const; - + std::vector show() const; + /// a function object method alternative to show() to display the filebox dialog, - bool operator()() const + std::vector operator()() const { return show(); } @@ -100,9 +98,12 @@ namespace nana explicit folderbox(window owner = nullptr, const path_type& init_path = {}, std::string title={}); ~folderbox(); - std::optional show() const; + /// Enables/disables multi select + void allow_multi_select(bool allow); - std::optional operator()() const + std::vector show() const; + + std::vector operator()() const { return show(); } diff --git a/source/gui/filebox.cpp b/source/gui/filebox.cpp index b830b8e5..239f9971 100644 --- a/source/gui/filebox.cpp +++ b/source/gui/filebox.cpp @@ -184,7 +184,7 @@ namespace nana throw std::runtime_error("Nana.GUI.Filebox: Wrong categorize path"); if(path.size() == 0) path = "/"; /// \todo : use nana::filesystem_ext::def_rootstr? - _m_load_cat_path(path); + _m_enter_folder(path); }); @@ -260,7 +260,7 @@ namespace nana selection_.is_deselect_delayed = false; } else if(event_code::dbl_click == arg.evt_code) - _m_click_select_file(arg); + _m_list_dbl_clicked(); }; ls_file_.events().dbl_click.connect_unignorable(fn_list_handler); @@ -354,6 +354,7 @@ namespace nana tb_file_.events().key_char.connect_unignorable([this](const arg_keyboard& arg) { + allow_fall_back_ = false; if(arg.key == nana::keyboard::enter) _m_try_select(tb_file_.caption()); }); @@ -444,7 +445,7 @@ namespace nana else dir = saved_selected_path; - _m_load_cat_path(dir.size() ? dir : fs_ext::path_user().native()); + _m_enter_folder(dir.size() ? dir : fs_ext::path_user().native()); tb_file_.caption(filename); } @@ -570,7 +571,7 @@ namespace nana { auto path = tree_.make_key_path(arg.item, "/") + "/"; _m_resolute_path(path); - _m_load_cat_path(path); + _m_enter_folder(path); } }); } @@ -630,7 +631,7 @@ namespace nana std::sort(file_container_.begin(), file_container_.end(), pred_sort_fs()); } - void _m_load_cat_path(std::string path) + void _m_enter_folder(std::string path) { if((path.size() == 0) || (path[path.size() - 1] != '/')) path += '/'; @@ -763,7 +764,7 @@ namespace nana { auto m = cat.append(fs); m.value(fs); - + if(fs.directory) m.icon(images_.folder); else @@ -791,7 +792,7 @@ namespace nana { auto ext4 = filename.substr(size - 4); - if( (".exe" == ext4) || + if( (".exe" == ext4) || (".dll" == ext4)) use_image = images_.exec; else if((".zip" == ext4) || (".rar" == ext4) || @@ -818,7 +819,7 @@ namespace nana else if(".html" == ext5) use_image = images_.xml; } - + if(use_image.empty()) m.icon(images_.file); else @@ -875,7 +876,7 @@ namespace nana return; } - fb_._m_load_cat_path(fb_.addr_.filesystem); + fb_._m_enter_folder(fb_.addr_.filesystem); fm_.close(); } @@ -968,22 +969,28 @@ namespace nana return false; } - void _m_click_select_file(const arg_mouse& arg) + //pos must be valid + item_fs _m_item_fs(const listbox::index_pair& pos) const + { + item_fs m; + ls_file_.at(pos).resolve_to(m); + return m; + } + + void _m_list_dbl_clicked() { auto selected_item = ls_file_.hovered(false); if(selected_item.empty()) return; - item_fs m; - ls_file_.at(selected_item).resolve_to(m); - - if(event_code::dbl_click == arg.evt_code) + item_fs m = _m_item_fs(selected_item); + if(m.directory) { - if(!m.directory) - _m_try_select(m.name); - else - _m_load_cat_path(addr_.filesystem + m.name + "/"); + _m_enter_folder(addr_.filesystem + m.name + "/"); + allow_fall_back_ = true; } + else + _m_try_select(m.name); } void _m_select_file(const listbox::item_proxy& item) @@ -1076,19 +1083,27 @@ namespace nana } return files; - } + void _m_msgbox(const char* i18n_idstr, const std::string& arg) const + { + internationalization i18n; + + msgbox mb(*this, caption()); + mb.icon(msgbox::icon_warning); + mb< files; + std::string init_file; std::string title; std::string path; @@ -1367,7 +1411,7 @@ namespace nana filebox& filebox::init_file(const std::string& ifstr) { - impl_->files = {ifstr}; + impl_->init_file = ifstr; return *this; } @@ -1383,23 +1427,12 @@ namespace nana return impl_->path; } - std::string filebox::file() const + std::vector filebox::show() const { - if(impl_->files.empty()) - return {}; + std::vector targets; - return impl_->files.front(); - } - - const std::vector& filebox::files() const - { - return impl_->files; - } - - bool filebox::show() const - { #if defined(NANA_WINDOWS) - std::wstring wfile(impl_->files.empty() ? L"" : to_wstring(impl_->files.front())); + std::wstring wfile = to_wstring(impl_->init_file); wfile.resize(impl_->allow_multi_select ? (520 + 32*256) : 520); OPENFILENAME ofn; @@ -1473,34 +1506,31 @@ namespace nana { internal_revert_guard revert; if (FALSE == (impl_->open_or_save ? ::GetOpenFileName(&ofn) : ::GetSaveFileName(&ofn))) - return false; + return targets; } if(impl_->allow_multi_select) { - wchar_t* str = ofn.lpstrFile; - std::wstring dir = str; - str += (dir.length() + 1); - impl_->files.clear(); + const wchar_t* str = ofn.lpstrFile; + auto len = ::wcslen(str); + + path_type parent_path{ str }; + str += (len + 1); + while(*str) { - std::wstring filename = str; - std::wstring file_path = dir + L"\\" + filename; - impl_->files.emplace_back(to_utf8(file_path)); - str += (filename.length() + 1); + len = ::wcslen(str); + targets.emplace_back(parent_path / path_type{str}); + str += (len + 1); } - impl_->path = to_utf8(dir); + impl_->path = parent_path.u8string(); } else { wfile.resize(std::wcslen(wfile.data())); - auto file = to_utf8(wfile); - impl_->files = {file}; - auto tpos = file.find_last_of("\\/"); - if(tpos != file.npos) - impl_->path = file.substr(0, tpos); - else - impl_->path.clear(); + + targets.emplace_back(wfile); + impl_->path = targets.front().parent_path().u8string(); } #elif defined(NANA_POSIX) @@ -1526,23 +1556,23 @@ namespace nana else fb.add_filter("All Files", "*.*"); - fb.load_fs(impl_->path, this->file()); + fb.load_fs(impl_->path, impl_->init_file); API::modal_window(fb); - fb.files().swap(impl_->files); - if(impl_->files.empty()) - return false; - auto tpos = impl_->files.front().find_last_of("\\/"); - if(tpos != impl_->files.front().npos) - impl_->path = impl_->files.front().substr(0, tpos); + for(auto & f : fb.files()) + targets.emplace_back(f); + + + if(!targets.empty()) + impl_->path = targets.front().parent_path().u8string(); else impl_->path.clear(); #endif - return true; + return targets; } - + void filebox::allow_multi_select(bool allow) { @@ -1556,11 +1586,11 @@ namespace nana window owner; path_type init_path; std::string title; - + bool allow_multi_select; }; folderbox::folderbox(window owner, const path_type& init_path, std::string title) - : impl_(new implement{ owner, fs::canonical(init_path).make_preferred(), title }) + : impl_(new implement{ owner, fs::canonical(init_path).make_preferred(), title, false}) {} @@ -1596,14 +1626,19 @@ namespace nana } #endif - std::optional folderbox::show() const + void folderbox::allow_multi_select(bool allow) { + impl_->allow_multi_select = allow; + } + + std::vector folderbox::show() const + { + std::vector targets; #ifdef NANA_WINDOWS - std::optional target; nana::detail::platform_spec::co_initializer co_init; #ifndef NANA_MINGW - IFileDialog *fd(nullptr); + IFileOpenDialog *fd(nullptr); HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&fd)); if (SUCCEEDED(hr)) { @@ -1612,21 +1647,32 @@ namespace nana if (SUCCEEDED(hr)) fd->SetFolder(init_path); - fd->SetOptions(FOS_PICKFOLDERS); + fd->SetOptions(FOS_PICKFOLDERS | (impl_->allow_multi_select ? FOS_ALLOWMULTISELECT : 0)); fd->Show(reinterpret_cast(API::root(impl_->owner))); // the native handle of the parent nana form goes here - IShellItem *si; - hr = fd->GetResult(&si); // fails if user cancelled - if (SUCCEEDED(hr)) + + ::IShellItemArray *sia; + if (SUCCEEDED(fd->GetResults(&sia))) // fails if user cancelled { - PWSTR pwstr(nullptr); - hr = si->GetDisplayName(SIGDN_FILESYSPATH, &pwstr); - if (SUCCEEDED(hr)) + DWORD num_items; + if (SUCCEEDED(sia->GetCount(&num_items))) { - target = path_type{ pwstr }; - // use the c-string pointed to by pwstr here - CoTaskMemFree(pwstr); + for (DWORD i = 0; i < num_items; ++i) + { + ::IShellItem* si; + if (SUCCEEDED(sia->GetItemAt(i, &si))) + { + PWSTR pwstr(nullptr); + if (SUCCEEDED(si->GetDisplayName(SIGDN_FILESYSPATH, &pwstr))) + { + targets.emplace_back(pwstr); + // use the c-string pointed to by pwstr here + ::CoTaskMemFree(pwstr); + } + si->Release(); + } + } } - si->Release(); + sia->Release(); } fd->Release(); } @@ -1649,27 +1695,25 @@ namespace nana { wchar_t folder_path[MAX_PATH]; if (FALSE != SHGetPathFromIDList(pidl, folder_path)) - target = folder_path; + targets.emplace_back(folder_path); co_init.task_mem_free(pidl); } #endif - return target; #elif defined(NANA_POSIX) using mode = filebox_implement::mode; - filebox_implement fb(impl_->owner, mode::open_directory, {}, true, false/*single select*/); + filebox_implement fb(impl_->owner, mode::open_directory, {}, true, impl_->allow_multi_select); fb.load_fs(impl_->init_path, ""); API::modal_window(fb); - std::string path_directory; - auto path_dir = fb.files(); - if(path_dir.empty()) - return {}; + auto path_dirs = fb.files(); - return path_type{path_dir.front()}; + for(auto & p: path_dirs) + targets.push_back(p); #endif + return targets; } }//end namespace nana diff --git a/source/gui/msgbox.cpp b/source/gui/msgbox.cpp index caf6380b..7263aa84 100644 --- a/source/gui/msgbox.cpp +++ b/source/gui/msgbox.cpp @@ -1228,9 +1228,10 @@ namespace nana impl->browse.events().click.connect_unignorable([wd, impl](const arg_click&) { impl->fbox.owner(wd); - if (impl->fbox.show()) + auto files = impl->fbox.show(); + if(!files.empty()) { - impl->value = impl->fbox.file(); + impl->value = files.front().u8string(); impl->path_edit.caption(impl->value); } }); diff --git a/source/internationalization.cpp b/source/internationalization.cpp index d41b775c..ba3ffcd6 100644 --- a/source/internationalization.cpp +++ b/source/internationalization.cpp @@ -193,9 +193,10 @@ namespace nana table["NANA_FILEBOX_ERROR_INVALID_FOLDER_NAME"] = "Please input a valid name for the new folder."; table["NANA_FILEBOX_ERROR_RENAME_FOLDER_BECAUSE_OF_EXISTING"] = "The folder is existing, please rename it."; table["NANA_FILEBOX_ERROR_RENAME_FOLDER_BECAUSE_OF_FAILED_CREATION"] = "Failed to create the folder, please rename it."; - table["NANA_FILEBOX_ERROR_INVALID_FILENAME"] = "The filename is invalid."; + table["NANA_FILEBOX_ERROR_INVALID_FILENAME"] = "\"%arg0\"\nThe filename is invalid."; table["NANA_FILEBOX_ERROR_NOT_EXISTING_AND_RETRY"] = "The file \"%arg0\"\n is not existing. Please check and retry."; table["NANA_FILEBOX_ERROR_DIRECTORY_NOT_EXISTING_AND_RETRY"] = "The directory \"%arg0\"\n is not existing. Please check and retry."; + table["NANA_FILEBOX_ERROR_DIRECTORY_INVALID"] = "The directory \"%arg0\"\n is invalid. Please check and retry."; table["NANA_FILEBOX_ERROR_QUERY_REWRITE_BECAUSE_OF_EXISTING"] = "The input file is existing, do you want to overwrite it?"; } }; From 71f57bbf9202ef2a11b7b80b3a97cb343c7f7200 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Thu, 28 Feb 2019 07:38:13 +0800 Subject: [PATCH 140/149] refactor filebox and folderbox --- include/nana/gui/filebox.hpp | 64 ++++++++++++++++++++---------------- source/gui/filebox.cpp | 44 ++++++++++++------------- 2 files changed, 57 insertions(+), 51 deletions(-) diff --git a/include/nana/gui/filebox.hpp b/include/nana/gui/filebox.hpp index b9c5bc84..3df2abb0 100644 --- a/include/nana/gui/filebox.hpp +++ b/include/nana/gui/filebox.hpp @@ -28,10 +28,8 @@ namespace nana filebox(filebox&&) = delete; filebox& operator=(filebox&&) = delete; public: - using filters = std::vector>; using path_type = std::filesystem::path; - explicit filebox(bool is_open_mode); filebox(window owner, bool is_open_mode); filebox(const filebox&); ~filebox(); @@ -39,20 +37,35 @@ namespace nana filebox& operator=(const filebox&); /// Change owner window - void owner(window); - - /// Set a new title for the dialog - /// @param string a text for title - /// @return the old title. - ::std::string title( ::std::string new_title); - - /** @brief Suggest initial path used to locate a directory when the filebox starts. - * @param string initial_directory a path of initial directory - * @note the behavior of init_path is different between Win7 and Win2K/XP/Vista, but its behavior under Linux is conformed with Win7. + /** + * Changes the owner window for the filebox. When #show()/operator()# are invoked, the dialog of filebox will be created with the specified owner. + * @param handle A handle to a window which will be used for the owner of filebox */ - filebox& init_path(const ::std::string& initial_directory); + void owner(window handle); - filebox& init_file(const ::std::string&); ///< Init file, if it contains a path, the init path is replaced by the path of init file. + /// Changes new title + /** + * Changes the title. When #show()/operator()# are invoked, the dialog of filebox will be created with the specified title. + * @param text Text of title + */ + void title( ::std::string text); + + /// Sets a initial path + /** + * Suggest initial path used to locate a directory when the filebox starts. + * @note the behavior of init_path is different between Win7 and Win2K/XP/Vista, but its behavior under Linux is conformed with Win7. + * @param path a path of initial directory + * @return reference of *this. + */ + filebox& init_path(const path_type& path); + + /// Sets a initial filename + /** + * Suggest a filename when filebox starts. If the filename contains a path, the initial path will be replaced with the path presents in initial filename. + * @param filename a filename used for a initial filename when filebox starts. + * @return reference of *this. + */ + filebox& init_file(const ::std::string& filename); ///< Init file, if it contains a path, the init path is replaced by the path of init file. /// \brief Add a filetype filter. /// To specify multiple filter in a single description, use a semicolon to separate the patterns(for example,"*.TXT;*.DOC;*.BAK"). @@ -60,17 +73,11 @@ namespace nana const ::std::string& filetype ///< filter pattern(for example: "*.TXT") ); - filebox& add_filter(const filters &ftres) - { - for (auto &f : ftres) - add_filter(f.first, f.second); - return *this; - }; + filebox& add_filter(const std::vector> &filters); - - const ::std::string& path() const; + const path_type& path() const; - void allow_multi_select(bool allow); + filebox& allow_multi_select(bool allow); /// Display the filebox dialog std::vector show() const; @@ -99,7 +106,7 @@ namespace nana ~folderbox(); /// Enables/disables multi select - void allow_multi_select(bool allow); + folderbox& allow_multi_select(bool allow); std::vector show() const; @@ -108,10 +115,11 @@ namespace nana return show(); } - /// Set a new title for the dialog - /// @param string a text for title - /// @return the old title. - ::std::string title( ::std::string new_title); + /// Changes title + /** + * @param text Text of title + */ + folderbox& title(std::string text); private: implement* impl_; }; diff --git a/source/gui/filebox.cpp b/source/gui/filebox.cpp index 239f9971..35fc8ca4 100644 --- a/source/gui/filebox.cpp +++ b/source/gui/filebox.cpp @@ -1339,15 +1339,10 @@ namespace nana std::string init_file; std::string title; - std::string path; + path_type path; std::vector filters; }; - filebox::filebox(bool is_openmode) - : filebox(nullptr, is_openmode) - { - } - filebox::filebox(window owner, bool open) : impl_(new implement) { @@ -1389,23 +1384,17 @@ namespace nana impl_->owner = wd; } - std::string filebox::title(std::string s) + void filebox::title(std::string s) { impl_->title.swap(s); - return s; } - filebox& filebox::init_path(const std::string& ipstr) + filebox& filebox::init_path(const path_type& p) { - if(ipstr.empty()) - { - impl_->path.clear(); - } - else - { - if (fs::is_directory(ipstr)) - impl_->path = ipstr; - } + std::error_code err; + if (p.empty() || is_directory(p, err)) + impl_->path = p; + return *this; } @@ -1422,7 +1411,14 @@ namespace nana return *this; } - const std::string& filebox::path() const + filebox& filebox::add_filter(const std::vector> &filters) + { + for (auto &f : filters) + add_filter(f.first, f.second); + return *this; + } + + const filebox::path_type& filebox::path() const { return impl_->path; } @@ -1574,9 +1570,10 @@ namespace nana } - void filebox::allow_multi_select(bool allow) + filebox& filebox::allow_multi_select(bool allow) { impl_->allow_multi_select = allow; + return *this; } //end class filebox @@ -1600,10 +1597,10 @@ namespace nana } - std::string folderbox::title(std::string s) + folderbox& folderbox::title(std::string s) { impl_->title.swap(s); - return s; + return *this; } @@ -1626,9 +1623,10 @@ namespace nana } #endif - void folderbox::allow_multi_select(bool allow) + folderbox& folderbox::allow_multi_select(bool allow) { impl_->allow_multi_select = allow; + return *this; } std::vector folderbox::show() const From f137f751c0d084df15b170277c35d39c08f3d50b Mon Sep 17 00:00:00 2001 From: Jinhao Date: Sun, 3 Mar 2019 07:07:58 +0800 Subject: [PATCH 141/149] refactor arg_dropfiles with std::filesystem::path --- include/nana/gui/detail/general_events.hpp | 9 +++++---- source/detail/platform_spec_posix.cpp | 4 ++-- source/detail/posix/msg_packet.hpp | 3 ++- source/gui/detail/bedrock_windows.cpp | 7 +++---- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/include/nana/gui/detail/general_events.hpp b/include/nana/gui/detail/general_events.hpp index 9a9496e2..e912d7b5 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-2018 Jinhao(cnjinhao@hotmail.com) +* Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -17,6 +17,7 @@ #include #include "event_code.hpp" #include "internal_scope_guard.hpp" +#include "../../filesystem/filesystem.hpp" #include #include #include @@ -470,9 +471,9 @@ namespace nana struct arg_dropfiles : public event_arg { - ::nana::window window_handle; ///< A handle to the event window - ::nana::point pos; ///< cursor position in the event window - std::vector files; ///< external filenames + ::nana::window window_handle; ///< A handle to the event window + ::nana::point pos; ///< cursor position in the event window + std::vector files; ///< external filenames }; struct arg_expose : public event_arg diff --git a/source/detail/platform_spec_posix.cpp b/source/detail/platform_spec_posix.cpp index bca68ea8..f7cbc9d6 100644 --- a/source/detail/platform_spec_posix.cpp +++ b/source/detail/platform_spec_posix.cpp @@ -1238,7 +1238,7 @@ namespace detail 0, AnyPropertyType, &type, &format, &len, &dummy_bytes_left, &data)) { - auto files = new std::vector; + auto files = new std::vector; std::stringstream ss(reinterpret_cast(data)); while(true) @@ -1258,7 +1258,7 @@ namespace detail break; } - files->push_back(file); + files->emplace_back(file); } if(files->size()) diff --git a/source/detail/posix/msg_packet.hpp b/source/detail/posix/msg_packet.hpp index 6a36f129..df7a4bcf 100644 --- a/source/detail/posix/msg_packet.hpp +++ b/source/detail/posix/msg_packet.hpp @@ -3,6 +3,7 @@ #include #include #include +#include namespace nana { @@ -29,7 +30,7 @@ namespace detail Window window; int x; int y; - std::vector * files; + std::vector * files; }mouse_drop; }u; }; diff --git a/source/gui/detail/bedrock_windows.cpp b/source/gui/detail/bedrock_windows.cpp index 30a05fb5..bd02a00d 100644 --- a/source/gui/detail/bedrock_windows.cpp +++ b/source/gui/detail/bedrock_windows.cpp @@ -1149,10 +1149,10 @@ namespace detail std::unique_ptr varbuf; std::size_t bufsize = 0; - unsigned size = ::DragQueryFile(drop, 0xFFFFFFFF, 0, 0); + unsigned size = ::DragQueryFile(drop, 0xFFFFFFFF, nullptr, 0); for(unsigned i = 0; i < size; ++i) { - unsigned reqlen = ::DragQueryFile(drop, i, 0, 0) + 1; + unsigned reqlen = ::DragQueryFile(drop, i, nullptr, 0) + 1; if(bufsize < reqlen) { varbuf.reset(new wchar_t[reqlen]); @@ -1160,8 +1160,7 @@ namespace detail } ::DragQueryFile(drop, i, varbuf.get(), reqlen); - - dropfiles.files.emplace_back(to_utf8(varbuf.get())); + dropfiles.files.emplace_back(varbuf.get()); } while(msgwnd && (msgwnd->flags.dropable == false)) From 92ae306994145b0b64a0e56aa76429e83cb8e250 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Tue, 5 Mar 2019 03:37:01 +0800 Subject: [PATCH 142/149] small change --- include/nana/gui/filebox.hpp | 3 ++- source/gui/filebox.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/include/nana/gui/filebox.hpp b/include/nana/gui/filebox.hpp index 3df2abb0..a2bfb4ef 100644 --- a/include/nana/gui/filebox.hpp +++ b/include/nana/gui/filebox.hpp @@ -47,8 +47,9 @@ namespace nana /** * Changes the title. When #show()/operator()# are invoked, the dialog of filebox will be created with the specified title. * @param text Text of title + * @return the reference of *this. */ - void title( ::std::string text); + filebox& title( ::std::string text); /// Sets a initial path /** diff --git a/source/gui/filebox.cpp b/source/gui/filebox.cpp index 35fc8ca4..b200ee07 100644 --- a/source/gui/filebox.cpp +++ b/source/gui/filebox.cpp @@ -1384,9 +1384,10 @@ namespace nana impl_->owner = wd; } - void filebox::title(std::string s) + filebox& filebox::title(std::string s) { impl_->title.swap(s); + return *this; } filebox& filebox::init_path(const path_type& p) From 26ae1bf32ed73fb28acbe3b83c8d30a065cbf0ce Mon Sep 17 00:00:00 2001 From: ErrorFlynn Date: Tue, 5 Mar 2019 08:33:05 -0500 Subject: [PATCH 143/149] OFN_FILEMUSTEXIST for filebox in open mode When the user types in the name of a nonexistent file, or selects a file and then changes the folder, the `OFN_FILEMUSTEXIST` flag causes the dialog box to pop up a warning message box when the user clicks the "Open" button, instead of closing and returning a nonexistent path. --- source/gui/filebox.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/source/gui/filebox.cpp b/source/gui/filebox.cpp index b200ee07..5b2db9e2 100644 --- a/source/gui/filebox.cpp +++ b/source/gui/filebox.cpp @@ -1494,6 +1494,7 @@ namespace nana if (!impl_->open_or_save) ofn.Flags = OFN_OVERWRITEPROMPT; //Overwrite prompt if it is save mode + else ofn.Flags = OFN_FILEMUSTEXIST; //In open mode, user can't type name of nonexistent file ofn.Flags |= OFN_NOCHANGEDIR; if(impl_->allow_multi_select) { From 0a3fb34e09ef6127381e4328b561aaeb1c3befdc Mon Sep 17 00:00:00 2001 From: Jinhao Date: Thu, 7 Mar 2019 01:10:02 +0800 Subject: [PATCH 144/149] use std::filesystem::path instead of std::string --- include/nana/filesystem/filesystem.hpp | 2 +- .../gui/widgets/skeletons/text_editor.hpp | 5 +- .../nana/gui/widgets/skeletons/textbase.hpp | 166 ++++++++---------- include/nana/gui/widgets/textbox.hpp | 12 +- source/filesystem/filesystem.cpp | 24 ++- source/gui/widgets/skeletons/text_editor.cpp | 2 +- source/gui/widgets/textbox.cpp | 21 +-- 7 files changed, 111 insertions(+), 121 deletions(-) diff --git a/include/nana/filesystem/filesystem.hpp b/include/nana/filesystem/filesystem.hpp index 83668943..f14c043f 100644 --- a/include/nana/filesystem/filesystem.hpp +++ b/include/nana/filesystem/filesystem.hpp @@ -428,7 +428,7 @@ namespace nana { namespace experimental { namespace filesystem file_status status(const path& p, std::error_code&); std::uintmax_t file_size(const path& p); - //uintmax_t file_size(const path& p, error_code& ec) noexcept; + std::uintmax_t file_size(const path& p, std::error_code& ec) noexcept; inline bool is_directory(file_status s) noexcept { return s.type() == file_type::directory ;} diff --git a/include/nana/gui/widgets/skeletons/text_editor.hpp b/include/nana/gui/widgets/skeletons/text_editor.hpp index 12b23ced..89b3651b 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-2018 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -66,6 +66,7 @@ namespace nana{ namespace widgets using char_type = wchar_t; using size_type = textbase::size_type; using string_type = textbase::string_type; + using path_type = std::filesystem::path; using event_interface = text_editor_event_interface; @@ -105,7 +106,7 @@ namespace nana{ namespace widgets void indent(bool, std::function generator); void set_event(event_interface*); - bool load(const char*); + bool load(const path_type& file); void text_align(::nana::align alignment); diff --git a/include/nana/gui/widgets/skeletons/textbase.hpp b/include/nana/gui/widgets/skeletons/textbase.hpp index d11bc872..130b1466 100644 --- a/include/nana/gui/widgets/skeletons/textbase.hpp +++ b/include/nana/gui/widgets/skeletons/textbase.hpp @@ -1,7 +1,7 @@ /* * A textbase class implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -18,10 +18,11 @@ #include #include #include +#include + #include "textbase_export_interface.hpp" #include -#include #include #include @@ -36,15 +37,16 @@ namespace skeletons : public ::nana::noncopyable { public: - typedef CharT char_type; - typedef std::basic_string string_type; - typedef typename string_type::size_type size_type; + using char_type = CharT; + using string_type = std::basic_string; + using size_type = typename string_type::size_type; + using path_type = std::filesystem::path; textbase() { attr_max_.reset(); //Insert an empty string for the first line of empty text. - text_cont_.emplace_back(new string_type); + text_cont_.emplace_back(); } void set_event_agent(textbase_event_agent_interface * evt) @@ -55,21 +57,19 @@ namespace skeletons bool empty() const { return (text_cont_.empty() || - ((text_cont_.size() == 1) && (text_cont_.front()->empty()))); + ((text_cont_.size() == 1) && text_cont_.front().empty())); } - bool load(const char* file_utf8) + bool load(const path_type& file) { - if (!file_utf8) - return false; - - std::ifstream ifs(to_osmbstr(file_utf8)); + std::ifstream ifs{ file.string() }; if (!ifs) return false; - ifs.seekg(0, std::ios::end); - std::size_t bytes = static_cast(ifs.tellg()); - ifs.seekg(0, std::ios::beg); + std::error_code err; + auto const bytes = file_size(file, err); + if (err) + return false; if(bytes >= 2) { @@ -81,7 +81,7 @@ namespace skeletons if(0xBB == ch && 0xBF == ifs.get()) { ifs.close(); - return load(file_utf8, nana::unicode::utf8); + return load(file, nana::unicode::utf8); } } else if(0xFF == ch) @@ -89,16 +89,13 @@ namespace skeletons if(0xFE == ifs.get()) { //UTF16,UTF32 - if(bytes >= 4) + if((bytes >= 4) && (ifs.get() == 0 && ifs.get() == 0)) { - if(ifs.get() == 0 && ifs.get() == 0) - { - ifs.close(); - return load(file_utf8, nana::unicode::utf32); - } + ifs.close(); + return load(file, nana::unicode::utf32); } ifs.close(); - return load(file_utf8, nana::unicode::utf16); + return load(file, nana::unicode::utf16); } } else if(0xFE == ch) @@ -107,7 +104,7 @@ namespace skeletons { //UTF16(big-endian) ifs.close(); - return load(file_utf8, nana::unicode::utf16); + return load(file, nana::unicode::utf16); } } else if(0 == ch) @@ -119,7 +116,7 @@ namespace skeletons { //UTF32(big_endian) ifs.close(); - return load(file_utf8, nana::unicode::utf32); + return load(file, nana::unicode::utf32); } } } @@ -135,15 +132,15 @@ namespace skeletons while(ifs.good()) { std::getline(ifs, str_mbs); - text_cont_.emplace_back(new string_type(static_cast(nana::charset{ str_mbs }))); - if(text_cont_.back()->size() > attr_max_.size) + text_cont_.emplace_back(static_cast(nana::charset{ str_mbs })); + if (text_cont_.back().size() > attr_max_.size) { - attr_max_.size = text_cont_.back()->size(); + attr_max_.size = text_cont_.back().size(); attr_max_.line = text_cont_.size() - 1; } } - _m_saved(file_utf8); + _m_saved(file); return true; } @@ -175,12 +172,9 @@ namespace skeletons } } - bool load(const char* file_utf8, nana::unicode encoding) + bool load(const path_type& file, nana::unicode encoding) { - if (!file_utf8) - return false; - - std::ifstream ifs(to_osmbstr(file_utf8)); + std::ifstream ifs{ file.string() }; if (!ifs) return false; @@ -218,9 +212,8 @@ namespace skeletons byte_order_translate_4bytes(str); } - text_cont_.emplace_back(new string_type(static_cast(nana::charset{ str, encoding }))); - - attr_max_.size = text_cont_.back()->size(); + text_cont_.emplace_back(static_cast(nana::charset{ str, encoding })); + attr_max_.size = text_cont_.back().size(); attr_max_.line = 0; } @@ -236,21 +229,21 @@ namespace skeletons byte_order_translate_4bytes(str); } - text_cont_.emplace_back(new string_type(static_cast(nana::charset{ str, encoding }))); - if(text_cont_.back()->size() > attr_max_.size) + text_cont_.emplace_back(static_cast(nana::charset{ str, encoding })); + if (text_cont_.back().size() > attr_max_.size) { - attr_max_.size = text_cont_.back()->size(); + attr_max_.size = text_cont_.back().size(); attr_max_.line = text_cont_.size() - 1; } } - _m_saved(file_utf8); + _m_saved(file); return true; } - void store(std::string fs, bool is_unicode, ::nana::unicode encoding) const + void store(const path_type& filename, bool is_unicode, ::nana::unicode encoding) const { - std::ofstream ofs(to_osmbstr(fs), std::ios::binary); + std::ofstream ofs(filename.string(), std::ios::binary); if(ofs && text_cont_.size()) { auto i = text_cont_.cbegin(); @@ -277,27 +270,26 @@ namespace skeletons for (std::size_t pos = 0; pos < count; ++pos) { - auto mbs = nana::charset(**(i++)).to_bytes(encoding); + auto mbs = nana::charset(*(i++)).to_bytes(encoding); ofs.write(mbs.c_str(), static_cast(mbs.size())); ofs.write("\r\n", 2); } - last_mbs = nana::charset(*text_cont_.back()).to_bytes(encoding); + last_mbs = nana::charset(text_cont_.back()).to_bytes(encoding); } else { for (std::size_t pos = 0; pos < count; ++pos) { - std::string mbs = nana::charset(**(i++)); + std::string mbs = nana::charset(*(i++)); ofs.write(mbs.c_str(), mbs.size()); ofs.write("\r\n", 2); } - - last_mbs = nana::charset(*text_cont_.back()); + last_mbs = nana::charset(text_cont_.back()); } ofs.write(last_mbs.c_str(), static_cast(last_mbs.size())); - _m_saved(std::move(fs)); + _m_saved(filename); } } @@ -311,7 +303,7 @@ namespace skeletons { if (!changed_) { - _m_first_change(); + _m_emit_first_change(); changed_ = true; } @@ -332,8 +324,7 @@ namespace skeletons const string_type& getline(size_type pos) const { if (pos < text_cont_.size()) - return *text_cont_[pos]; - + return text_cont_[pos]; return nullstr_; } @@ -346,7 +337,7 @@ namespace skeletons { if (text_cont_.size() <= pos) { - text_cont_.emplace_back(new string_type(std::move(text))); + text_cont_.emplace_back(std::move(text)); pos = text_cont_.size() - 1; } else @@ -369,7 +360,7 @@ namespace skeletons } else { - text_cont_.emplace_back(new string_type(std::move(str))); + text_cont_.emplace_back(std::move(str)); pos.y = static_cast(text_cont_.size() - 1); } @@ -380,9 +371,9 @@ namespace skeletons void insertln(size_type pos, string_type&& str) { if (pos < text_cont_.size()) - text_cont_.emplace(_m_iat(pos), new string_type(std::move(str))); + text_cont_.emplace(_m_iat(pos), std::move(str)); else - text_cont_.emplace_back(new string_type(std::move(str))); + text_cont_.emplace_back(std::move(str)); _m_make_max(pos); edited_ = true; @@ -429,9 +420,9 @@ namespace skeletons { text_cont_.clear(); attr_max_.reset(); - text_cont_.emplace_back(new string_type); //text_cont_ must not be empty + text_cont_.emplace_back(); //text_cont_ must not be empty - _m_saved(std::string()); + _m_saved({}); } void merge(size_type pos) @@ -439,7 +430,8 @@ namespace skeletons if(pos + 1 < text_cont_.size()) { auto i = _m_iat(pos + 1); - _m_at(pos) += **i; + _m_at(pos) += *i; + text_cont_.erase(i); _m_make_max(pos); @@ -453,7 +445,7 @@ namespace skeletons } } - const std::string& filename() const + const path_type& filename() const { return filename_; } @@ -463,33 +455,25 @@ namespace skeletons return changed_; } - void edited_reset() + void reset_status(bool remain_saved_filename) { - changed_ = false; - } + if(!remain_saved_filename) + filename_.clear(); - void reset() - { - filename_.clear(); changed_ = false; } bool saved() const { - return ! not_saved(); - } - - bool not_saved() const - { - return edited() || filename_.empty(); + return !(changed_ || filename_.empty()); } private: string_type& _m_at(size_type pos) { - return **_m_iat(pos); + return *_m_iat(pos); } - typename std::deque>::iterator _m_iat(size_type pos) + typename std::deque::iterator _m_iat(size_type pos) { return text_cont_.begin() + pos; } @@ -506,50 +490,40 @@ namespace skeletons void _m_scan_for_max() { - attr_max_.size = 0; - std::size_t n = 0; - for(auto & p : text_cont_) - { - if(p->size() > attr_max_.size) - { - attr_max_.size = p->size(); - attr_max_.line = n; - } - ++n; - } + attr_max_.reset(); + for (std::size_t i = 0; i < text_cont_.size(); ++i) + _m_make_max(i); } - void _m_first_change() const + void _m_emit_first_change() const { if (evt_agent_) evt_agent_->first_change(); } - void _m_saved(std::string && filename) const + void _m_saved(const path_type& filename) const { - if(filename_ != filename) + if((filename_ != filename) || changed_) { - filename_ = std::move(filename); - _m_first_change(); + filename_ = filename; + _m_emit_first_change(); } - else if(changed_) - _m_first_change(); changed_ = false; } private: - std::deque> text_cont_; + std::deque text_cont_; textbase_event_agent_interface* evt_agent_{ nullptr }; mutable bool changed_{ false }; mutable bool edited_{ false }; - mutable std::string filename_; //A string for the saved filename. + mutable path_type filename_; ///< The saved filename const string_type nullstr_; struct attr_max { - std::size_t line; - std::size_t size; + std::size_t line; ///< The line number of max line + std::size_t size; ///< The number of characters in max line void reset() { diff --git a/include/nana/gui/widgets/textbox.hpp b/include/nana/gui/widgets/textbox.hpp index 5f245761..8bf34b38 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-2018 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -109,6 +109,8 @@ namespace nana using text_focus_behavior = widgets::skeletons::text_focus_behavior; using text_positions = std::vector; + using path_type = std::filesystem::path; + /// The default constructor without creating the widget. textbox(); @@ -136,9 +138,9 @@ namespace nana textbox(window, const rectangle& = rectangle(), bool visible = true); /// \brief Loads a text file. When attempt to load a unicode encoded text file, be sure the file have a BOM header. - void load(std::string file); - void store(std::string file); - void store(std::string file, nana::unicode encoding); + void load(const path_type& file); + void store(const path_type& file); + void store(const path_type& file, nana::unicode encoding); colored_area_access_interface* colored_area_access(); @@ -158,7 +160,7 @@ namespace nana textbox& reset(const std::string& text = std::string(), bool end_caret = true); ///< discard the old text and set a new text /// The file of last store operation. - std::string filename() const; + path_type filename() const; /// Determine whether the text was edited. bool edited() const; diff --git a/source/filesystem/filesystem.cpp b/source/filesystem/filesystem.cpp index 4ec85bf5..e1a59d80 100644 --- a/source/filesystem/filesystem.cpp +++ b/source/filesystem/filesystem.cpp @@ -936,7 +936,7 @@ namespace nana { namespace experimental { namespace filesystem auto stat = status(p, err); if (err != std::error_code()) - throw filesystem_error("nana::experimental::filesystem::status", p, err); + throw filesystem_error("nana::filesystem::status", p, err); return stat; } @@ -1002,6 +1002,16 @@ namespace nana { namespace experimental { namespace filesystem std::uintmax_t file_size(const path& p) { + std::error_code err; + auto bytes = file_size(p, err); + if (err) + throw filesystem_error("nana::filesystem::status", p, err); + + return bytes; + } + + std::uintmax_t file_size(const path& p, std::error_code& ec) + { #if defined(NANA_WINDOWS) //Some compilation environment may fail to link to GetFileSizeEx typedef BOOL(__stdcall *GetFileSizeEx_fptr_t)(HANDLE, PLARGE_INTEGER); @@ -1019,23 +1029,25 @@ namespace nana { namespace experimental { namespace filesystem return li.QuadPart; } } - return 0; + ec.assign(static_cast(::GetLastError()), std::generic_category()); #elif defined(NANA_POSIX) FILE * stream = ::fopen(p.c_str(), "rb"); - long long size = 0; if (stream) { + long long bytes = 0; # if defined(NANA_LINUX) fseeko64(stream, 0, SEEK_END); - size = ftello64(stream); + bytes = ftello64(stream); # elif defined(NANA_POSIX) fseeko(stream, 0, SEEK_END); - size = ftello(stream); + bytes = ftello(stream); # endif ::fclose(stream); + return bytes; } - return size; + ec.assign(static_cast(::errno), std::generic_category()); #endif + return static_cast(-1); } diff --git a/source/gui/widgets/skeletons/text_editor.cpp b/source/gui/widgets/skeletons/text_editor.cpp index b0c4c800..f5af56a6 100644 --- a/source/gui/widgets/skeletons/text_editor.cpp +++ b/source/gui/widgets/skeletons/text_editor.cpp @@ -1275,7 +1275,7 @@ namespace nana { event_handler_ = ptr; } - bool text_editor::load(const char* fs) + bool text_editor::load(const path_type& fs) { if (!impl_->textbase.load(fs)) return false; diff --git a/source/gui/widgets/textbox.cpp b/source/gui/widgets/textbox.cpp index 8695f1e5..6e0a0ecc 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-2018 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -233,31 +233,31 @@ namespace drawerbase { create(wd, r, visible); } - void textbox::load(std::string file) + void textbox::load(const std::filesystem::path& file) { internal_scope_guard lock; auto editor = get_drawer_trigger().editor(); - if (editor && editor->load(file.data())) + if (editor && editor->load(file)) { if (editor->try_refresh()) API::update_window(handle()); } } - void textbox::store(std::string file) + void textbox::store(const std::filesystem::path& file) { internal_scope_guard lock; auto editor = get_drawer_trigger().editor(); if (editor) - editor->textbase().store(std::move(file), false, nana::unicode::utf8); //3rd parameter is just for syntax, it will be ignored + editor->textbase().store(file, false, nana::unicode::utf8); //3rd parameter is just for syntax, it will be ignored } - void textbox::store(std::string file, nana::unicode encoding) + void textbox::store(const std::filesystem::path& file, nana::unicode encoding) { internal_scope_guard lock; auto editor = get_drawer_trigger().editor(); if (editor) - editor->textbase().store(std::move(file), true, encoding); + editor->textbase().store(file, true, encoding); } textbox::colored_area_access_interface* textbox::colored_area_access() @@ -300,7 +300,8 @@ namespace drawerbase { if (end_caret) editor->move_caret_end(true); - editor->textbase().reset(); + //Reset the edited status and the saved filename + editor->textbase().reset_status(false); if (editor->try_refresh()) API::update_window(this->handle()); @@ -308,7 +309,7 @@ namespace drawerbase { return *this; } - std::string textbox::filename() const + textbox::path_type textbox::filename() const { internal_scope_guard lock; auto editor = get_drawer_trigger().editor(); @@ -330,7 +331,7 @@ namespace drawerbase { internal_scope_guard lock; auto editor = get_drawer_trigger().editor(); if (editor) - editor->textbase().edited_reset(); + editor->textbase().reset_status(true); return *this; } From fd8261c6a2b0aee6f7d6149258a55a862e88d915 Mon Sep 17 00:00:00 2001 From: ErrorFlynn Date: Thu, 7 Mar 2019 04:36:27 -0500 Subject: [PATCH 145/149] fix for issue #399 --- source/gui/widgets/group.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/source/gui/widgets/group.cpp b/source/gui/widgets/group.cpp index f1ca7821..9bb62ff1 100644 --- a/source/gui/widgets/group.cpp +++ b/source/gui/widgets/group.cpp @@ -235,6 +235,14 @@ checkbox& group::add_option(std::string text) group& group::enable_format_caption(bool format) { impl_->caption.format(format); + + // if the caption is already set, make sure the layout is updated + if(!caption().empty()) + { + impl_->update_div(); + impl_->place_content.collocate(); + API::refresh_window(*this); + } return *this; } From 845dda8c3ea898a73cbc46ab08948b9708aa8f4a Mon Sep 17 00:00:00 2001 From: ErrorFlynn Date: Thu, 7 Mar 2019 12:42:59 -0500 Subject: [PATCH 146/149] corrected wrong offset for "Exif" bytes --- source/paint/image.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/paint/image.cpp b/source/paint/image.cpp index 1781a657..53b71227 100644 --- a/source/paint/image.cpp +++ b/source/paint/image.cpp @@ -228,7 +228,7 @@ namespace paint //JFIF if (bytes > 11 && (0xe0ffd8ff == *reinterpret_cast(data)) && 0x4649464A == *reinterpret_cast(reinterpret_cast(data)+6)) ptr = std::make_shared(); - else if (bytes > 9 && (0x66697845 == *reinterpret_cast(reinterpret_cast(data)+5))) //Exif + else if (bytes > 9 && (0x66697845 == *reinterpret_cast(reinterpret_cast(data)+6))) //Exif ptr = std::make_shared(); #endif if ((!ptr) && (bytes > 40)) From 2abae1dd68d73bd3cc3dd4a6e821213ace9c84a4 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Fri, 8 Mar 2019 06:11:21 +0800 Subject: [PATCH 147/149] refactoring --- include/nana/gui/detail/general_events.hpp | 32 ++---- include/nana/gui/detail/window_manager.hpp | 6 +- include/nana/gui/programming_interface.hpp | 3 +- .../gui/widgets/skeletons/text_editor.hpp | 11 +- source/gui/detail/inner_fwd_implement.hpp | 2 +- source/gui/detail/native_window_interface.cpp | 2 + source/gui/detail/window_manager.cpp | 101 +++++------------- source/gui/dragger.cpp | 10 +- source/gui/programming_interface.cpp | 44 ++++++-- source/gui/widgets/skeletons/text_editor.cpp | 80 ++++---------- source/gui/widgets/textbox.cpp | 4 +- 11 files changed, 106 insertions(+), 189 deletions(-) diff --git a/include/nana/gui/detail/general_events.hpp b/include/nana/gui/detail/general_events.hpp index e912d7b5..c2595fe5 100644 --- a/include/nana/gui/detail/general_events.hpp +++ b/include/nana/gui/detail/general_events.hpp @@ -298,34 +298,16 @@ namespace nana }; } - static std::function build_second(fn_type&& fn, void(fn_type::*)(arg_reference)) + template + static std::function build_second(Tfn&& fn, Ret(fn_type::*)(arg_reference)) { - return std::move(fn); + return std::forward(fn); } - static std::function build_second(fn_type&& fn, void(fn_type::*)(arg_reference) const) + template + static std::function build_second(Tfn&& fn, Ret(fn_type::*)(arg_reference)const) { - return std::move(fn); - } - - static std::function build_second(fn_type& fn, void(fn_type::*)(arg_reference)) - { - return fn; - } - - static std::function build_second(fn_type& fn, void(fn_type::*)(arg_reference) const) - { - return fn; - } - - static std::function build_second(const fn_type& fn, void(fn_type::*)(arg_reference)) - { - return fn; - } - - static std::function build_second(const fn_type& fn, void(fn_type::*)(arg_reference) const) - { - return fn; + return std::forward(fn); } template @@ -421,7 +403,7 @@ namespace nana typedef typename std::remove_reference::type arg_type; static_assert(std::is_convertible::value, "The parameter type is not allowed, please check the function parameter type where you connected the event function."); - static std::function build(Ret(*fn)(Arg)) + static std::function build(Ret(*fn)(Arg2)) { return[fn](arg_reference arg){ fn(arg); diff --git a/include/nana/gui/detail/window_manager.hpp b/include/nana/gui/detail/window_manager.hpp index 474cd2ea..82e3c57c 100644 --- a/include/nana/gui/detail/window_manager.hpp +++ b/include/nana/gui/detail/window_manager.hpp @@ -118,10 +118,6 @@ namespace detail void do_lazy_refresh(core_window_t*, bool force_copy_to_screen, bool refresh_tree = false); void map_requester(core_window_t*); - bool get_graphics(core_window_t*, nana::paint::graphics&); - bool get_visual_rectangle(core_window_t*, nana::rectangle&); - - std::vector get_children(core_window_t*) const; bool set_parent(core_window_t* wd, core_window_t* new_parent); core_window_t* set_focus(core_window_t*, bool root_has_been_focused, arg_focus::reason); @@ -144,7 +140,6 @@ namespace detail bool register_shortkey(core_window_t*, unsigned long key); void unregister_shortkey(core_window_t*, bool with_children); - std::vector> shortkeys(core_window_t*, bool with_children); core_window_t* find_shortkey(native_window_type, unsigned long key); @@ -154,6 +149,7 @@ namespace detail void _m_disengage(core_window_t*, core_window_t* for_new); void _m_destroy(core_window_t*); void _m_move_core(core_window_t*, const point& delta); + void _m_shortkeys(core_window_t*, bool with_chlidren, std::vector>& keys) const; core_window_t* _m_find(core_window_t*, const point&); static bool _m_effective(core_window_t*, const point& root_pos); private: diff --git a/include/nana/gui/programming_interface.hpp b/include/nana/gui/programming_interface.hpp index 2fdd3c14..48a773ce 100644 --- a/include/nana/gui/programming_interface.hpp +++ b/include/nana/gui/programming_interface.hpp @@ -230,7 +230,6 @@ namespace API window root(native_window_type); ///< Retrieves the native window of a Nana.GUI window. void fullscreen(window, bool); - bool enabled_double_click(window, bool); void close_window(window); void show_window(window, bool show); ///< Sets a window visible state. @@ -239,6 +238,7 @@ namespace API bool visible(window); window get_parent_window(window); window get_owner_window(window); + bool set_parent_window(window, window new_parent); template @@ -435,6 +435,7 @@ namespace API /// Sets the window active state. If a window active state is false, the window will not obtain the focus when a mouse clicks on it wich will be obteined by take_if_has_active_false. void take_active(window, bool has_active, window take_if_has_active_false); + /// Copies the graphics of a specified to a new graphics object. bool window_graphics(window, nana::paint::graphics&); bool root_graphics(window, nana::paint::graphics&); bool get_visual_rectangle(window, nana::rectangle&); diff --git a/include/nana/gui/widgets/skeletons/text_editor.hpp b/include/nana/gui/widgets/skeletons/text_editor.hpp index 89b3651b..5f691543 100644 --- a/include/nana/gui/widgets/skeletons/text_editor.hpp +++ b/include/nana/gui/widgets/skeletons/text_editor.hpp @@ -136,13 +136,11 @@ namespace nana{ namespace widgets void enable_background(bool); void enable_background_counterpart(bool); - void undo_enabled(bool); - bool undo_enabled() const; + void undo_clear(); void undo_max_steps(std::size_t); std::size_t undo_max_steps() const; renderers& customized_renderers(); - void clear_undo(); ///< same with undo_max_steps(0) unsigned line_height() const; unsigned screen_lines(bool completed_line = false) const; @@ -203,12 +201,11 @@ namespace nana{ namespace widgets void del(); void backspace(bool record_undo, bool perform_event); void undo(bool reverse); - void set_undo_queue_length(std::size_t len); void move_ns(bool to_north); //Moves up and down void move_left(); void move_right(); const upoint& mouse_caret(const point& screen_pos, bool stay_in_view); - const upoint& caret() const; + const upoint& caret() const noexcept; point caret_screen_pos() const; bool scroll(bool upwards, bool vertical); @@ -218,8 +215,8 @@ namespace nana{ namespace widgets void mouse_pressed(const arg_mouse& arg); bool select_word(const arg_mouse& arg); - skeletons::textbase& textbase(); - const skeletons::textbase& textbase() const; + skeletons::textbase& textbase() noexcept; + const skeletons::textbase& textbase() const noexcept; bool try_refresh(); diff --git a/source/gui/detail/inner_fwd_implement.hpp b/source/gui/detail/inner_fwd_implement.hpp index ed8ef73d..5c66323c 100644 --- a/source/gui/detail/inner_fwd_implement.hpp +++ b/source/gui/detail/inner_fwd_implement.hpp @@ -46,7 +46,7 @@ namespace nana{ void umake(window wd); - std::vector keys(window wd) const; + const std::vector* keys(window wd) const; window find(unsigned long key) const; private: diff --git a/source/gui/detail/native_window_interface.cpp b/source/gui/detail/native_window_interface.cpp index a9753bc7..2d2d0e58 100644 --- a/source/gui/detail/native_window_interface.cpp +++ b/source/gui/detail/native_window_interface.cpp @@ -1444,6 +1444,8 @@ namespace nana{ { if(owner) return owner; + + return x11_parent_window(wd); } else if(window_relationship::owner == rsp) return owner; diff --git a/source/gui/detail/window_manager.cpp b/source/gui/detail/window_manager.cpp index 78ea0927..6349685d 100644 --- a/source/gui/detail/window_manager.cpp +++ b/source/gui/detail/window_manager.cpp @@ -111,17 +111,17 @@ namespace nana } } - std::vector shortkey_container::keys(window wd) const + const std::vector* shortkey_container::keys(window wd) const { if (wd) { for (auto & m : impl_->base) { if (m.handle == wd) - return m.keys; + return &m.keys; } } - return{}; + return nullptr; } window shortkey_container::find(unsigned long key) const @@ -1111,41 +1111,6 @@ namespace detail wd->other.mapping_requester.clear(); } - //get_graphics - //@brief: Get a copy of the graphics object of a window. - // the copy of the graphics object has a same buf handle with the graphics object's, they are count-refered - // here returns a reference that because the framework does not guarantee the wnd's - // graphics object available after a get_graphics call. - bool window_manager::get_graphics(core_window_t* wd, nana::paint::graphics& result) - { - //Thread-Safe Required! - std::lock_guard lock(mutex_); - if (!impl_->wd_register.available(wd)) - return false; - - result.make(wd->drawer.graphics.size()); - result.bitblt(0, 0, wd->drawer.graphics); - window_layer::paste_children_to_graphics(wd, result); - return true; - } - - bool window_manager::get_visual_rectangle(core_window_t* wd, nana::rectangle& r) - { - //Thread-Safe Required! - std::lock_guard lock(mutex_); - return (impl_->wd_register.available(wd) ? - window_layer::read_visual_rectangle(wd, r) : - false); - } - - std::vector window_manager::get_children(core_window_t* wd) const - { - std::lock_guard lock(mutex_); - if (impl_->wd_register.available(wd)) - return wd->children; - return{}; - } - bool window_manager::set_parent(core_window_t* wd, core_window_t* newpa) { //Thread-Safe Required! @@ -1351,7 +1316,6 @@ namespace detail } } - // preconditions of get_tabstop: tabstop is not empty and at least one window is visible window_manager::core_window_t* get_tabstop(window_manager::core_window_t* wd, bool forward) { @@ -1464,9 +1428,8 @@ namespace detail std::lock_guard lock(mutex_); if (impl_->wd_register.available(wd)) { - auto object = root_runtime(wd->root); - if(object) - return object->shortkeys.make(reinterpret_cast(wd), key); + //the root runtime must exist, because the wd is valid. Otherse, it's bug of library + return root_runtime(wd->root)->shortkeys.make(reinterpret_cast(wd), key); } return false; } @@ -1489,35 +1452,6 @@ namespace detail } } - auto window_manager::shortkeys(core_window_t* wd, bool with_children) -> std::vector> - { - std::vector> result; - - //Thread-Safe Required! - std::lock_guard lock(mutex_); - if (impl_->wd_register.available(wd)) - { - auto root_rt = root_runtime(wd->root); - if (root_rt) - { - auto keys = root_rt->shortkeys.keys(reinterpret_cast(wd)); - for (auto key : keys) - result.emplace_back(wd, key); - - if (with_children) - { - for (auto child : wd->children) - { - auto child_keys = shortkeys(child, true); - std::copy(child_keys.begin(), child_keys.end(), std::back_inserter(result)); - } - } - } - } - - return result; - } - window_manager::core_window_t* window_manager::find_shortkey(native_window_type native_window, unsigned long key) { if(native_window) @@ -1577,7 +1511,6 @@ namespace detail { auto * const wdpa = wd->parent; - bool established = (for_new && (wdpa != for_new)); decltype(for_new->root_widget->other.attribute.root) pa_root_attr = nullptr; @@ -1603,7 +1536,7 @@ namespace detail if (root_attr->menubar && check_tree(wd, root_attr->menubar)) root_attr->menubar = nullptr; - sk_holder = shortkeys(wd, true); + _m_shortkeys(wd, true, sk_holder); } else { @@ -1815,6 +1748,28 @@ namespace detail } } + void window_manager::_m_shortkeys(core_window_t* wd, bool with_children, std::vector>& keys) const + { + if (impl_->wd_register.available(wd)) + { + //The root_rt must exist, because wd is valid. Otherwise, it's a bug of the library. + auto root_rt = root_runtime(wd->root); + + auto pkeys = root_rt->shortkeys.keys(reinterpret_cast(wd)); + if (pkeys) + { + for (auto key : *pkeys) + keys.emplace_back(wd, key); + } + + if (with_children) + { + for (auto child : wd->children) + _m_shortkeys(child, true, keys); + } + } + } + //_m_find //@brief: find a window on root window through a given root coordinate. // the given root coordinate must be in the rectangle of wnd. diff --git a/source/gui/dragger.cpp b/source/gui/dragger.cpp index 44ea2254..946273c7 100644 --- a/source/gui/dragger.cpp +++ b/source/gui/dragger.cpp @@ -1,7 +1,7 @@ /* * A Dragger Implementation * Nana C++ Library(http://www.nanapro.org) -* Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) +* Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -93,9 +93,7 @@ namespace nana for (auto & t : targets_) { t.origin = API::window_position(t.wd); - window owner = API::get_owner_window(t.wd); - if (owner) - API::calc_screen_point(owner, t.origin); + API::calc_screen_point(API::get_owner_window(t.wd), t.origin); } break; case event_code::mouse_move: @@ -108,10 +106,8 @@ namespace nana { if (API::is_window_zoomed(t.wd, true) == false) { - auto owner = API::get_owner_window(t.wd); auto wdps = t.origin; - if (owner) - API::calc_window_point(owner, wdps); + API::calc_window_point(API::get_owner_window(t.wd), wdps); switch (t.move_direction) { diff --git a/source/gui/programming_interface.cpp b/source/gui/programming_interface.cpp index 3218069d..148667ff 100644 --- a/source/gui/programming_interface.cpp +++ b/source/gui/programming_interface.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -90,16 +91,21 @@ namespace API void enum_widgets_function_base::enum_widgets(window wd, bool recursive) { - using basic_window = ::nana::detail::basic_window; + auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - - auto children = restrict::wd_manager().get_children(reinterpret_cast(wd)); - for (auto child : children) + if (restrict::wd_manager().available(iwd)) { - auto widget_ptr = API::get_widget(reinterpret_cast(child)); - if (widget_ptr) + //Use a copy, because enum function may close a child window and the original children container would be changed, + //in the situation, the walking thorugh directly to the iwd->children would cause error. + auto children = iwd->children; + + for (auto child : children) { + auto widget_ptr = API::get_widget(reinterpret_cast(child)); + if (!widget_ptr) + continue; + _m_enum_fn(widget_ptr); if (recursive) enum_widgets(reinterpret_cast(child), recursive); @@ -643,7 +649,15 @@ namespace API auto iwd = reinterpret_cast(wd); internal_scope_guard lock; if (restrict::wd_manager().available(iwd)) + { + if (category::flags::root == iwd->other.category) + { + return reinterpret_cast(restrict::wd_manager().root( + interface_type::get_window(iwd->root, window_relationship::parent) + )); + } return reinterpret_cast(iwd->parent); + } return nullptr; } @@ -1308,7 +1322,16 @@ namespace API bool window_graphics(window wd, nana::paint::graphics& graph) { - return restrict::wd_manager().get_graphics(reinterpret_cast(wd), graph); + auto iwd = reinterpret_cast(wd); + + internal_scope_guard lock; + if (!restrict::wd_manager().available(iwd)) + return false; + + graph.make(iwd->drawer.graphics.size()); + graph.bitblt(0, 0, iwd->drawer.graphics); + nana::detail::window_layout::paste_children_to_graphics(iwd, graph); + return true; } bool root_graphics(window wd, nana::paint::graphics& graph) @@ -1325,7 +1348,12 @@ namespace API bool get_visual_rectangle(window wd, nana::rectangle& r) { - return restrict::wd_manager().get_visual_rectangle(reinterpret_cast(wd), r); + auto iwd = reinterpret_cast(wd); + internal_scope_guard lock; + if (restrict::wd_manager().available(iwd)) + return nana::detail::window_layout::read_visual_rectangle(iwd, r); + + return false; } void typeface(window wd, const nana::paint::font& font) diff --git a/source/gui/widgets/skeletons/text_editor.cpp b/source/gui/widgets/skeletons/text_editor.cpp index f5af56a6..d3878e75 100644 --- a/source/gui/widgets/skeletons/text_editor.cpp +++ b/source/gui/widgets/skeletons/text_editor.cpp @@ -49,7 +49,7 @@ namespace nana { using command = EnumCommand; using container = std::deque>>; - void clear() + void clear() noexcept { commands_.clear(); pos_ = 0; @@ -67,21 +67,9 @@ namespace nana { return max_steps_; } - void enable(bool enb) - { - enabled_ = enb; - if (!enb) - clear(); - } - - bool enabled() const - { - return enabled_; - } - void push(std::unique_ptr> && ptr) { - if (!ptr || !enabled_) + if (!ptr) return; if (pos_ < commands_.size()) @@ -100,7 +88,7 @@ namespace nana { ++pos_; } - std::size_t count(bool is_undo) const + std::size_t count(bool is_undo) const noexcept { return (is_undo ? pos_ : commands_.size() - pos_); } @@ -108,10 +96,7 @@ namespace nana { void undo() { if (pos_ > 0) - { - --pos_; - commands_[pos_].get()->execute(false); - } + commands_[--pos_].get()->execute(false); } void redo() @@ -122,7 +107,6 @@ namespace nana { private: container commands_; - bool enabled_{ true }; std::size_t max_steps_{ 30 }; std::size_t pos_{ 0 }; }; @@ -763,9 +747,7 @@ namespace nana { if (second < linemtr_.size()) linemtr_.erase(linemtr_.begin() + first + 1, linemtr_.begin() + second + 1); - auto const width_px = editor_.width_pixels(); - - pre_calc_line(first, width_px); + pre_calc_line(first, editor_.width_pixels()); } void add_lines(std::size_t pos, std::size_t lines) override @@ -1074,8 +1056,8 @@ namespace nana { //class text_editor - text_editor::text_editor(window wd, graph_reference graph, const text_editor_scheme* schm) - : impl_(new implementation), + text_editor::text_editor(window wd, graph_reference graph, const text_editor_scheme* schm): + impl_(new implementation), window_(wd), graph_(graph), scheme_(schm) @@ -1400,14 +1382,11 @@ namespace nana { impl_->counterpart.buffer.release(); } - void text_editor::undo_enabled(bool enb) + void text_editor::undo_clear() { - impl_->undo.enable(enb); - } - - bool text_editor::undo_enabled() const - { - return impl_->undo.enabled(); + auto size = this->undo_max_steps(); + impl_->undo.max_steps(0); + impl_->undo.max_steps(size); } void text_editor::undo_max_steps(std::size_t maxs) @@ -1420,13 +1399,6 @@ namespace nana { return impl_->undo.max_steps(); } - void text_editor::clear_undo() - { - auto size = impl_->undo.max_steps(); - impl_->undo.max_steps(0); - impl_->undo.max_steps(size); - } - auto text_editor::customized_renderers() -> renderers& { return impl_->customized_renderers; @@ -1435,12 +1407,11 @@ namespace nana { unsigned text_editor::line_height() const { unsigned ascent, descent, internal_leading; - unsigned px = 0; - if (graph_.text_metrics(ascent, descent, internal_leading)) - px = ascent + descent; + if (!graph_.text_metrics(ascent, descent, internal_leading)) + return 0; - impl_->cview->step(px, false); - return px; + impl_->cview->step(ascent + descent, false); + return ascent + descent; } unsigned text_editor::screen_lines(bool completed_line) const @@ -1449,13 +1420,7 @@ namespace nana { if (line_px) { auto h = impl_->cview->view_area().height; - if (graph_ && h) - { - if (completed_line) - return (h / line_px); - - return (h / line_px + (h % line_px ? 1 : 0)); - } + return (h / line_px) + ((completed_line || !(h % line_px)) ? 0 : 1); } return 0; } @@ -1652,12 +1617,12 @@ namespace nana { return true; } - textbase & text_editor::textbase() + textbase & text_editor::textbase() noexcept { return impl_->textbase; } - const textbase & text_editor::textbase() const + const textbase & text_editor::textbase() const noexcept { return impl_->textbase; } @@ -2327,11 +2292,6 @@ namespace nana { impl_->try_refresh = sync_graph::refresh; } - void text_editor::set_undo_queue_length(std::size_t len) - { - impl_->undo.max_steps(len); - } - void text_editor::move_ns(bool to_north) { const bool redraw_required = _m_cancel_select(0); @@ -2566,7 +2526,7 @@ namespace nana { return points_.caret; } - const upoint& text_editor::caret() const + const upoint& text_editor::caret() const noexcept { return points_.caret; } @@ -3344,7 +3304,7 @@ namespace nana { int text_editor::_m_text_topline() const { auto px = static_cast(line_height()); - return (px ? (impl_->cview->origin().y / px) : px); + return (px ? (impl_->cview->origin().y / px) : 0); } int text_editor::_m_text_x(const text_section& sct) const diff --git a/source/gui/widgets/textbox.cpp b/source/gui/widgets/textbox.cpp index 6e0a0ecc..6ea644e2 100644 --- a/source/gui/widgets/textbox.cpp +++ b/source/gui/widgets/textbox.cpp @@ -671,7 +671,7 @@ namespace drawerbase { void textbox::clear_undo() { - get_drawer_trigger().editor()->clear_undo(); + get_drawer_trigger().editor()->undo_clear(); } void textbox::set_highlight(const std::string& name, const ::nana::color& fgcolor, const ::nana::color& bgcolor) @@ -792,7 +792,7 @@ namespace drawerbase { internal_scope_guard lock; auto editor = get_drawer_trigger().editor(); if (editor) - editor->set_undo_queue_length(len); + editor->undo_max_steps(len); } std::size_t textbox::display_line_count() const noexcept From 0d96fcc097ccdafd7680681c5341ef32c178f3f9 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Sat, 9 Mar 2019 18:12:47 +0800 Subject: [PATCH 148/149] refactoring general events --- include/nana/gui/detail/general_events.hpp | 32 +++++----------------- source/gui/detail/events_operation.cpp | 6 ++-- 2 files changed, 11 insertions(+), 27 deletions(-) diff --git a/include/nana/gui/detail/general_events.hpp b/include/nana/gui/detail/general_events.hpp index c2595fe5..2171cb7a 100644 --- a/include/nana/gui/detail/general_events.hpp +++ b/include/nana/gui/detail/general_events.hpp @@ -47,12 +47,11 @@ namespace nana struct docker_base : public docker_interface { - event_interface * event_ptr; - bool flag_deleted{ false }; + event_interface * const event_ptr; + bool flag_deleted; const bool unignorable; docker_base(event_interface*, bool unignorable_flag); - detail::event_interface * get_event() const override; }; @@ -118,7 +117,7 @@ namespace nana private: struct docker : public detail::docker_base - { + { /// the callback/response function taking the typed argument std::function invoke; @@ -221,33 +220,16 @@ namespace nana //The dockers may resize when a new event handler is created by a calling handler. //Traverses with position can avaid crash error which caused by a iterator which becomes invalid. - - auto i = dockers_->data(); - auto const end = i + dockers_->size(); - - for (; i != end; ++i) + for (std::size_t i = 0; i < dockers_->size(); ++i) { - if (static_cast(*i)->flag_deleted) + auto d = static_cast(dockers_->data()[i]); + if (d->flag_deleted || (arg.propagation_stopped() && !d->unignorable)) continue; - static_cast(*i)->invoke(arg); + d->invoke(arg); if (window_handle && (!detail::check_window(window_handle))) break; - - if (arg.propagation_stopped()) - { - for (++i; i != end; ++i) - { - if (!static_cast(*i)->unignorable || static_cast(*i)->flag_deleted) - continue; - - static_cast(*i)->invoke(arg); - if (window_handle && (!detail::check_window(window_handle))) - break; - } - break; - } } } private: diff --git a/source/gui/detail/events_operation.cpp b/source/gui/detail/events_operation.cpp index 40fe338e..a278bc96 100644 --- a/source/gui/detail/events_operation.cpp +++ b/source/gui/detail/events_operation.cpp @@ -34,8 +34,10 @@ namespace nana //class docker_base - docker_base::docker_base(event_interface* evt, bool unignorable_flag) - : event_ptr(evt), unignorable(unignorable_flag) + docker_base::docker_base(event_interface* evt, bool unignorable_flag): + event_ptr(evt), + flag_deleted(false), + unignorable(unignorable_flag) {} detail::event_interface * docker_base::get_event() const From f8604c06c8b32c468efcbd35e93f08eee625e567 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Sat, 9 Mar 2019 22:55:37 +0800 Subject: [PATCH 149/149] improve jpeg check --- source/paint/image.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/source/paint/image.cpp b/source/paint/image.cpp index 868f925f..857c31f7 100644 --- a/source/paint/image.cpp +++ b/source/paint/image.cpp @@ -1,7 +1,7 @@ /* * Paint Image Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -225,11 +225,16 @@ namespace paint else { #if defined(NANA_ENABLE_JPEG) - //JFIF - if (bytes > 11 && (0xe0ffd8ff == *reinterpret_cast(data)) && 0x4649464A == *reinterpret_cast(reinterpret_cast(data)+6)) - ptr = std::make_shared(); - else if (bytes > 9 && (0x66697845 == *reinterpret_cast(reinterpret_cast(data)+6))) //Exif - ptr = std::make_shared(); + if ((bytes > 11) && (0xd8ff == *reinterpret_cast(data))) + { + switch(*reinterpret_cast(reinterpret_cast(data)+6)) + { + case 0x4649464A: //JFIF + case 0x66697845: //Exif + ptr = std::make_shared(); + } + } + else #endif if ((!ptr) && (bytes > 40)) {