diff --git a/.gitignore b/.gitignore index ef6de4d4..e46bb0b6 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,7 @@ build/makefile-bkl/* *.ilk *.log [Bb]in +[Bb]in/ [Dd]ebug*/ *.lib *.a diff --git a/.travis.yml b/.travis.yml index 02ceda02..170ef287 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,6 +34,7 @@ matrix: - alsa-oss - libx11-dev - libxft-dev + - libboost-filesystem-dev sources: - ubuntu-toolchain-r-test @@ -55,7 +56,7 @@ matrix: - llvm-toolchain-precise before_install: - - git clone --depth=50 --branch=master https://github.com/qPCR4vir/nana-demo.git nana-demo + - git clone --depth=1 --branch=develop https://github.com/qPCR4vir/nana-demo.git ../nana-demo - export PATH="$HOME/bin:$PATH" - mkdir ~/bin - 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 @@ -65,10 +66,92 @@ install: - /tmp/tools/cmake --prefix="$HOME" --exclude-subdir before_script : - - mkdir bld - - cd bld + # travis don't have a physical monitor. We need to install an emulator: https://docs.travis-ci.com/user/gui-and-headless-browsers/ + - "export DISPLAY=:99.0" + - "sh -e /etc/init.d/xvfb start" + - 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: - - cmake -G"Unix Makefiles" .. -DENABLE_JPEG=ON -DENABLE_PNG=OFF -DBUILD_NANA_DEMOS=ON -DENABLE_AUDIO=OFF - - - make + # 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 + - cd .. + - ls + - cd .. + - 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 + - make install + # todo: separate resources from sources (a directory for images) + - ls + - cd ../bin + - ls + - ./a_group_impl + - ./animate-bmp + - ./audio_player + - ./background-effects + #- ./calculator # https://travis-ci.org/qPCR4vir/nana/jobs/140238537#L1159 + - ./categ + - ./clicked + - ./decore + - ./dock + - ./drag-button + - ./draw + - ./file_explorer + #- ./example_menu # https://travis-ci.org/qPCR4vir/nana/jobs/140238537#L1348 + - ./example_listbox + #- ./example_combox # https://travis-ci.org/qPCR4vir/nana/jobs/140238537#L1378 + - ./example.button + #- ./filebox-txt # https://travis-ci.org/qPCR4vir/nana/jobs/140250744#L1393 + - ./folder_tree + #- ./folder_tree_nana # https://travis-ci.org/qPCR4vir/nana/jobs/140238537#L1408 + #- ./folder_tree_std # https://travis-ci.org/qPCR4vir/nana/jobs/140238537#L1421 + - ./framework_design_1 + - ./framework_design_2 + - ./framework_design_3 + - ./group + - ./HelloWord + #- ./helloword_quit # https://travis-ci.org/qPCR4vir/nana/jobs/140238537#L1572 + #- ./inputbox # https://travis-ci.org/qPCR4vir/nana/jobs/140238537#L1585 + - ./label_listener + - ./lambda_event.Cpp11 + - ./listbox_inline_widget + - ./listbox_Resolver + - ./loader_1 + #- ./loader_2 # https://travis-ci.org/qPCR4vir/nana/jobs/140238537#L1732 + - ./mbox + - ./main + - ./menu_debug + #- ./modal_form # https://travis-ci.org/qPCR4vir/nana/jobs/140250744#L1736 + #- ./MontiHall # https://travis-ci.org/qPCR4vir/nana/jobs/140238537#L1775 + #- ./helloworld_demo # https://travis-ci.org/qPCR4vir/nana/jobs/140238537#L1786 + #- ./notepad # https://travis-ci.org/qPCR4vir/nana/jobs/140238537#L1799 + - ./menu_debug + - ./menu_popuper + #- ./modal_form # https://travis-ci.org/qPCR4vir/nana/jobs/140238537#L1846 + #- ./widget_show2 # https://travis-ci.org/qPCR4vir/nana/jobs/140245437#L1730 + #- ./widget_show # https://travis-ci.org/qPCR4vir/nana/jobs/140245437#L1740 + - ./place_login + - ./png + #- ./screen # https://travis-ci.org/qPCR4vir/nana/jobs/140238537#L1909 + - ./stretch_image + #- ./threading # https://travis-ci.org/qPCR4vir/nana/jobs/140245437#L1826 ? + #- ./thread-pool # https://travis-ci.org/qPCR4vir/nana/jobs/140247564#L1782 + - ./various_events + #- ./window-dragger # https://travis-ci.org/qPCR4vir/nana/jobs/140245438#L1820 + - ./windows-subclassing + - ./textbox_line_number + + diff --git a/CMakeLists.txt b/CMakeLists.txt index f4b6ec4f..5c9c9c97 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,68 +1,71 @@ # CMake configuration for Nana # Author: Andrew Kornilov(https://github.com/ierofant) -# Contributor: -# Robert Hauck - Enable support for PNG/Freetype +# Contributors: +# Jinhao +# Robert Hauck - Enable support for PNG/Freetype # Qiangqiang Wu - Add biicode support # Ariel Vina-Rodriguez (qPCR4vir) +# +# 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 +# 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 -# use CACHE FORCE or set(ENABLE_MINGW_STD_THREADS_WITH_MEGANZ ON) or delete CMakecache.txt or the entirely build dir +# 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 -option(ENABLE_MINGW_STD_THREADS_WITH_MEGANZ "replaced boost.thread with meganz's mingw-std-threads." OFF) -option(ENABLE_PNG "Enable the use of PNG" OFF) -option(LIBPNG_FROM_OS "Use libpng from operating system." ON) -option(ENABLE_JPEG "Enable the use of JPEG" OFF) -option(LIBJPEG_FROM_OS "Use libjpeg from operating system." ON) -option(ENABLE_AUDIO "Enable class audio::play for PCM playback." OFF) -option(CMAKE_VERBOSE_PREPROCESSOR "Show annoying debug messages during compilation." OFF) -option(CMAKE_STOP_VERBOSE_PREPROCESSOR "Stop compilation after showing the annoying debug messages." ON) -option(BUILD_NANA_DEMOS "Build all the demos form the nana_demo repository." OFF) -# The ISO C++ File System Technical Specification is optional. +# 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) + +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) +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 -# The selected option will be set by nana into std::experimental::filesystem -# By default Nana will use the ISO TS if available, or nana if not. -# Boost will be use only if you change one of the following: -option(CMAKE_BOOST_FILESYSTEM_AVAILABLE "Is Boost filesystem available?" OFF) -option(NANA_BOOST_FILESYSTEM_PREFERRED "Is Boost filesystem preferred over nana?" OFF) -option(CMAKE_BOOST_FILESYSTEM_FORCE "Force use of Boost filesystem if available (over ISO)?" OFF) -option(CMAKE_BOOST_FILESYSTEM_INCLUDE_ROOT "Where to find ?" "../") -option(CMAKE_BOOST_FILESYSTEM_LIB "Flag for the compiler to link: " "-lboost/fs") +# 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) -set(NANA_LINKS) - -if (CMAKE_BOOST_FILESYSTEM_AVAILABLE) - if (CMAKE_BOOST_FILESYSTEM_PREFERED OR CMAKE_BOOST_FILESYSTEM_FORCE) - add_definitions(-DNANA_BOOST_FILESYSTEM_AVAILABLE) - if (CMAKE_BOOST_FILESYSTEM_FORCE) - add_definitions(-DNANA_BOOST_FILESYSTEM_FORCE) - else() - add_definitions(-DNANA_BOOST_FILESYSTEM_PREFERED) - endif() - include_directories("${CMAKE_BOOST_FILESYSTEM_INCLUDE_ROOT}") - list(APPEND NANA_LINKS "${CMAKE_BOOST_FILESYSTEM_LIB}") - endif (CMAKE_BOOST_FILESYSTEM_PREFERED OR CMAKE_BOOST_FILESYSTEM_FORCE) -endif (CMAKE_BOOST_FILESYSTEM_AVAILABLE) - -project(nana) -cmake_minimum_required(VERSION 2.8) - -# Compatibility with CMake 3.1 +########### 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() -add_definitions(-DNANA_IGNORE_CONF) +########### OS if(WIN32) add_definitions(-DWIN32) - set(BUILD_FreeMe ON) #"Build FreeMe only on Windows." #Global MSVC definitions. You may prefer the hand-tuned sln and projects from the nana repository. if(MSVC) option(WIN32_USE_MP "Set to ON to build nana with the /MP option (Visual Studio 2005 and above)." ON) @@ -73,325 +76,219 @@ if(WIN32) endif(MSVC) if(MINGW) - if(ENABLE_MINGW_STD_THREADS_WITH_MEGANZ) + if(NANA_CMAKE_ENABLE_MINGW_STD_THREADS_WITH_MEGANZ) + add_definitions(-DSTD_THREAD_NOT_SUPPORTED) add_definitions(-DNANA_ENABLE_MINGW_STD_THREADS_WITH_MEGANZ) - endif(ENABLE_MINGW_STD_THREADS_WITH_MEGANZ) + endif(NANA_CMAKE_ENABLE_MINGW_STD_THREADS_WITH_MEGANZ) endif(MINGW) -elseif(WIN32) - set(BUILD_FreeMe OFF) endif(WIN32) if(APPLE) add_definitions(-DAPPLE) include_directories(/opt/X11/include/) - list(APPEND NANA_LINKS -L/opt/X11/lib/ -liconv) + set(NANA_LINKS "${NANA_LINKS} -L/opt/X11/lib/ -liconv") set(ENABLE_AUDIO OFF) elseif(UNIX) add_definitions(-Dlinux) message("added -D linux") endif(APPLE) - - if(UNIX) - list(APPEND NANA_LINKS -lX11 ) + set(NANA_LINKS "${NANA_LINKS} -lX11") find_package(Freetype) if (FREETYPE_FOUND) include_directories( ${FREETYPE_INCLUDE_DIRS}) - list(APPEND NANA_LINKS -lXft ) + set(NANA_LINKS "${NANA_LINKS} -lXft") endif(FREETYPE_FOUND) endif(UNIX) -#Find PNG -if(ENABLE_PNG) + +########### Compilers +# +# Using gcc: gcc 4.8 don't support C++14 and make_unique. You may want to update at least to 4.9. +# In Windows, the gcc which come with CLion was 4.8 from MinGW. You may want to install MinGW-w64 from the +# TDM-GCC Compiler Suite for Windows which will update you to gcc 5.1. +# gcc 5.3 and 5.4 include filesytem, but you need to add the link flag: -lstdc++fs +# +# see at end of: https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_dynamic_or_shared.html +if(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") # Clang || GNU + + if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++14 -Wall -g") # Clang + + else ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall -g") # GNU + + endif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") + +endif(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") + +# enable static linkage # GNU || CLang not MinGW +if(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") # AND NOT MINGW + # set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++ -pthread") + set(NANA_LINKS "${NANA_LINKS} -static-libgcc -static-libstdc++ -pthread") + # message("Setting NANA_LINKS to -static-libgcc -static-libstdc++ -pthread or ${NANA_LINKS}") + if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.3) + # IS_GNUCXX < 5.3 + else(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.3) + # set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lstdc++fs") # IS_GNUCXX 5.3 or more + set(NANA_LINKS "${NANA_LINKS} -lstdc++fs") + endif(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.3) + +endif(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") # AND NOT MINGW + + +if (APPLE AND "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") # APPLE Clang + # set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libstdc++") + set(NANA_LINKS "${NANA_LINKS} -stdlib=libstdc++") +endif () + + +############# Optional libraries + +# Find PNG +if(NANA_CMAKE_ENABLE_PNG) add_definitions(-DNANA_ENABLE_PNG) - #set(NANA_PNG_LIB "png") - list(APPEND NANA_LINKS -lpng ) - if(LIBPNG_FROM_OS) + set(NANA_LINKS "${NANA_LINKS} -lpng") + if(NANA_CMAKE_LIBPNG_FROM_OS) find_package(PNG) if (PNG_FOUND) include_directories( ${PNG_INCLUDE_DIRS}) add_definitions(-DUSE_LIBPNG_FROM_OS) endif(PNG_FOUND) - endif(LIBPNG_FROM_OS) -endif(ENABLE_PNG) + endif(NANA_CMAKE_LIBPNG_FROM_OS) +endif(NANA_CMAKE_ENABLE_PNG) -#Find JPEG -if(ENABLE_JPEG) +# Find JPEG +if(NANA_CMAKE_ENABLE_JPEG) add_definitions(-DNANA_ENABLE_JPEG) - #set(NANA_JPEG_LIB "jpeg") - list(APPEND NANA_LINKS -ljpeg ) - if(LIBJPEG_FROM_OS) + set(NANA_LINKS "${NANA_LINKS} -ljpeg") + if(NANA_CMAKE_LIBJPEG_FROM_OS) find_package(JPEG) if (JPEG_FOUND) include_directories( ${JPEG_INCLUDE_DIRS}) add_definitions(-DUSE_LIBJPEG_FROM_OS) endif(JPEG_FOUND) - endif(LIBJPEG_FROM_OS) -endif(ENABLE_JPEG) + endif(NANA_CMAKE_LIBJPEG_FROM_OS) +endif(NANA_CMAKE_ENABLE_JPEG) -if(ENABLE_AUDIO) +# Find ASOUND +if(NANA_CMAKE_ENABLE_AUDIO) add_definitions(-DNANA_ENABLE_AUDIO) if(UNIX) find_package(ASOUND) if (ASOUND_FOUND) include_directories( ${ASOUND_INCLUDE_DIRS}) - list(APPEND NANA_LINKS -lasound ) + set(NANA_LINKS "${NANA_LINKS} -lasound") else(ASOUND_FOUND) message(FATAL_ERROR "libasound is not found") endif(ASOUND_FOUND) endif(UNIX) -endif(ENABLE_AUDIO) +endif(NANA_CMAKE_ENABLE_AUDIO) -#Unicode -if(CMAKE_VERBOSE_PREPROCESSOR) +# Find/Select filesystem +if (NANA_CMAKE_NANA_FILESYSTEM_FORCE) + add_definitions(-DNANA_FILESYSTEM_FORCE) +elseif (NANA_CMAKE_STD_FILESYSTEM_FORCE) + add_definitions(-DSTD_FILESYSTEM_FORCE) +elseif (NANA_CMAKE_FIND_BOOST_FILESYSTEM OR NANA_CMAKE_BOOST_FILESYSTEM_FORCE) + if (NANA_CMAKE_BOOST_FILESYSTEM_FORCE) + add_definitions(-DNANA_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, + # 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(-DNANA_BOOST_FILESYSTEM_AVAILABLE) + include_directories(SYSTEM "${Boost_INCLUDE_DIR}") + set(NANA_LINKS "${NANA_LINKS} ${Boost_LIBRARIES}") ###### FIRST !!!!!!!!!!!!!!!!! add is not first + endif (Boost_FOUND) + set(Boost_USE_STATIC_LIBS ON) + set(Boost_USE_STATIC_RUNTIME ON) # ?? +endif (NANA_CMAKE_NANA_FILESYSTEM_FORCE) + + +######## Nana options + +add_definitions(-DNANA_IGNORE_CONF) +if(NANA_CMAKE_VERBOSE_PREPROCESSOR) add_definitions(-DVERBOSE_PREPROCESSOR) -endif(CMAKE_VERBOSE_PREPROCESSOR) +endif(NANA_CMAKE_VERBOSE_PREPROCESSOR) +if(NANA_CMAKE_AUTOMATIC_GUI_TESTING) + add_definitions(-DNANA_AUTOMATIC_GUI_TESTING) + enable_testing () +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}) include_directories(${NANA_INCLUDE_DIR}) -aux_source_directory(${NANA_SOURCE_DIR} NANA_SOURCE) -aux_source_directory(${NANA_SOURCE_DIR}/detail NANA_DETAIL_SOURCE) -aux_source_directory(${NANA_SOURCE_DIR}/filesystem NANA_FILESYSTEM_SOURCE) -aux_source_directory(${NANA_SOURCE_DIR}/gui NANA_GUI_SOURCE) -aux_source_directory(${NANA_SOURCE_DIR}/gui/detail NANA_GUI_DETAIL_SOURCE) -aux_source_directory(${NANA_SOURCE_DIR}/gui/widgets NANA_GUI_WIDGETS_SOURCE) -aux_source_directory(${NANA_SOURCE_DIR}/gui/widgets/skeletons NANA_GUI_WIDGETS_SKELETONS_SOURCE) -aux_source_directory(${NANA_SOURCE_DIR}/paint NANA_PAINT_SOURCE) -aux_source_directory(${NANA_SOURCE_DIR}/paint/detail NANA_PAINT_DETAIL_SOURCE) -aux_source_directory(${NANA_SOURCE_DIR}/system NANA_SYSTEM_SOURCE) -aux_source_directory(${NANA_SOURCE_DIR}/threads NANA_THREADS_SOURCE) -if(ENABLE_AUDIO) - aux_source_directory(${NANA_SOURCE_DIR}/audio NANA_AUDIO_SOURCE) - aux_source_directory(${NANA_SOURCE_DIR}/audio/detail NANA_AUDIO_DETAIL_SOURCE) -endif() +add_library(${PROJECT_NAME} ${sources} ) +target_link_libraries(${PROJECT_NAME} ${NANA_LINKS}) -#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 + # Headers: use INCLUDE_DIRECTORIES + # Libraries: use FIND_LIBRARY and link with the result of it (try to avoid LINK_DIRECTORIES) -add_library(${PROJECT_NAME} ${NANA_SOURCE} - ${NANA_DETAIL_SOURCE} - ${NANA_FILESYSTEM_SOURCE} - ${NANA_AUDIO_SOURCE} - ${NANA_AUDIO_DETAIL_SOURCE} - ${NANA_GUI_SOURCE} - ${NANA_GUI_DETAIL_SOURCE} - ${NANA_GUI_WIDGETS_SOURCE} - ${NANA_GUI_WIDGETS_SKELETONS_SOURCE} - ${NANA_PAINT_SOURCE} - ${NANA_PAINT_DETAIL_SOURCE} - ${NANA_SYSTEM_SOURCE} - ${NANA_THREADS_SOURCE}) - - #Headers: use INCLUDE_DIRECTORIES - # Libraries: use FIND_LIBRARY and link with the result of it (try to avoid LINK_DIRECTORIES - target_link_libraries(${PROJECT_NAME} ${NANA_LINKS}) - - -install(TARGETS ${PROJECT_NAME} - ARCHIVE DESTINATION lib - LIBRARY DESTINATION lib) -# ?? -install(DIRECTORY ${NANA_INCLUDE_DIR}/nana DESTINATION include) +# 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/ +install(TARGETS ${PROJECT_NAME} ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib) +message("The compiled Nana library will be installed in ${CMAKE_INSTALL_PREFIX}/lib") +# Install the include directories too. +if(NANA_CMAKE_INSTALL_INCLUDES) + install(DIRECTORY ${NANA_INCLUDE_DIR}/nana DESTINATION include ) + message("The Nana include files will be installed in ${CMAKE_INSTALL_PREFIX}/include") +endif(NANA_CMAKE_INSTALL_INCLUDES) set_property( TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 14 ) - -# TODO: move this nana-demo section to the nana demo repository, and here only include that cmake file - -if (BUILD_NANA_DEMOS) - set (CMAKE_INSTALL_PREFIX ${DEMO_BIN}) - set(DEMO_BIN ${NANA_SOURCE_DIR}../nana-demo/bin) - set(CMAKE_INSTALL_PREFIX ) - - add_executable(calculator ../nana-demo/calculator.cpp) - set_property( TARGET calculator PROPERTY CXX_STANDARD 14 ) - target_link_libraries(calculator ${PROJECT_NAME} ) - install(TARGETS calculator RUNTIME DESTINATION &{DEMO_BIN}) - - if (BUILD_FreeMe) - add_executable(FreeMe ../nana-demo/FreeMe.cpp) - set_property( TARGET FreeMe PROPERTY CXX_STANDARD 14 ) - target_link_libraries(FreeMe ${PROJECT_NAME} ) - install(TARGETS FreeMe RUNTIME DESTINATION &{DEMO_BIN}) - endif (BUILD_FreeMe) - - add_executable(notepad ../nana-demo/notepad.cpp) - set_property( TARGET notepad PROPERTY CXX_STANDARD 14 ) - target_link_libraries(notepad ${PROJECT_NAME} ) - install(TARGETS notepad RUNTIME DESTINATION &{DEMO_BIN}) - - add_executable(widget_show ../nana-demo/widget_show.cpp) - set_property( TARGET widget_show PROPERTY CXX_STANDARD 14 ) - target_link_libraries(widget_show ${PROJECT_NAME}) - install(TARGETS widget_show RUNTIME DESTINATION &{DEMO_BIN}) - - add_executable(widget_show2 ../nana-demo/widget_show2.cpp) - set_property( TARGET widget_show2 PROPERTY CXX_STANDARD 14 ) - target_link_libraries(widget_show2 ${PROJECT_NAME}) - install(TARGETS widget_show2 RUNTIME DESTINATION &{DEMO_BIN}) - - if (OFF) # temporal: we need to adapt the use of filesystem to nana v1.03 (no file iterator) - - add_executable(file_explorer ../nana-demo/file_explorer.cpp) - set_property( TARGET file_explorer PROPERTY CXX_STANDARD 14 ) - target_link_libraries(file_explorer ${PROJECT_NAME} ) - install(TARGETS file_explorer RUNTIME DESTINATION &{DEMO_BIN}) - - - - endif(OFF) - - add_executable(a_group_impl ../nana-demo/Examples/a_group_impl.cpp) - set_property( TARGET a_group_impl PROPERTY CXX_STANDARD 14 ) - target_link_libraries(a_group_impl ${PROJECT_NAME}) - - add_executable(animate-bmp ../nana-demo/Examples/animate-bmp.cpp) - set_property( TARGET animate-bmp PROPERTY CXX_STANDARD 14 ) - target_link_libraries(animate-bmp ${PROJECT_NAME}) - - add_executable(background-effects ../nana-demo/Examples/background-effects.cpp) - set_property( TARGET background-effects PROPERTY CXX_STANDARD 14 ) - target_link_libraries(background-effects ${PROJECT_NAME}) - - add_executable(categ ../nana-demo/Examples/categ.cpp) - set_property( TARGET categ PROPERTY CXX_STANDARD 14 ) - target_link_libraries(categ ${PROJECT_NAME}) - - add_executable(clicked ../nana-demo/Examples/clicked.cpp) - set_property( TARGET clicked PROPERTY CXX_STANDARD 14 ) - target_link_libraries(clicked ${PROJECT_NAME}) - - add_executable(decore ../nana-demo/Examples/decore.cpp) - set_property( TARGET decore PROPERTY CXX_STANDARD 14 ) - target_link_libraries(decore ${PROJECT_NAME}) - - add_executable(dock ../nana-demo/Examples/dock.cpp) - set_property( TARGET dock PROPERTY CXX_STANDARD 14 ) - target_link_libraries(dock ${PROJECT_NAME}) - - add_executable(drag-button ../nana-demo/Examples/drag-button.cpp) - set_property( TARGET drag-button PROPERTY CXX_STANDARD 14 ) - target_link_libraries(drag-button ${PROJECT_NAME}) - - add_executable(draw ../nana-demo/Examples/draw.cpp) - set_property( TARGET draw PROPERTY CXX_STANDARD 14 ) - target_link_libraries(draw ${PROJECT_NAME}) - - add_executable(example_combox ../nana-demo/Examples/example_combox.cpp) - set_property( TARGET example_combox PROPERTY CXX_STANDARD 14 ) - target_link_libraries(example_combox ${PROJECT_NAME}) - - add_executable(example_listbox ../nana-demo/Examples/example_listbox.cpp) - set_property( TARGET example_listbox PROPERTY CXX_STANDARD 14 ) - target_link_libraries(example_listbox ${PROJECT_NAME}) - - add_executable(example_menu ../nana-demo/Examples/example_menu.cpp) - set_property( TARGET example_menu PROPERTY CXX_STANDARD 14 ) - target_link_libraries(example_menu ${PROJECT_NAME}) - - add_executable(filebox-txt ../nana-demo/Examples/filebox-txt.cpp) - set_property( TARGET filebox-txt PROPERTY CXX_STANDARD 14 ) - target_link_libraries(filebox-txt ${PROJECT_NAME}) - - add_executable(folder_tree ../nana-demo/Examples/folder_tree.cpp) - set_property( TARGET folder_tree PROPERTY CXX_STANDARD 14 ) - target_link_libraries(folder_tree ${PROJECT_NAME}) - - add_executable(folder_tree_nana ../nana-demo/Examples/folder_tree_nana.cpp) - set_property( TARGET folder_tree_nana PROPERTY CXX_STANDARD 14 ) - target_link_libraries(folder_tree_nana ${PROJECT_NAME}) - - add_executable(folder_tree_std ../nana-demo/Examples/folder_tree_std.cpp) - set_property( TARGET folder_tree_std PROPERTY CXX_STANDARD 14 ) - target_link_libraries(folder_tree_std ${PROJECT_NAME}) - - add_executable(listbox_Resolver ../nana-demo/Examples/listbox_Resolver.cpp) - set_property( TARGET listbox_Resolver PROPERTY CXX_STANDARD 14 ) - target_link_libraries(listbox_Resolver ${PROJECT_NAME}) - - add_executable(framework_design_2 ../nana-demo/Examples/framework_design_2.cpp) - set_property( TARGET framework_design_2 PROPERTY CXX_STANDARD 14 ) - target_link_libraries(framework_design_2 ${PROJECT_NAME}) - - add_executable(framework_design_3 ../nana-demo/Examples/framework_design_3.cpp) - set_property( TARGET framework_design_3 PROPERTY CXX_STANDARD 14 ) - target_link_libraries(framework_design_3 ${PROJECT_NAME}) - - add_executable(group ../nana-demo/Examples/group.cpp) - set_property( TARGET group PROPERTY CXX_STANDARD 14 ) - target_link_libraries(group ${PROJECT_NAME}) - - add_executable(HelloWord ../nana-demo/Examples/HelloWord.cpp) - set_property( TARGET HelloWord PROPERTY CXX_STANDARD 14 ) - target_link_libraries(HelloWord ${PROJECT_NAME}) - - add_executable(listbox_inline_widget ../nana-demo/Examples/listbox_inline_widget.cpp) - set_property( TARGET listbox_inline_widget PROPERTY CXX_STANDARD 14 ) - target_link_libraries(listbox_inline_widget ${PROJECT_NAME}) - - add_executable(inputbox ../nana-demo/Examples/inputbox.cpp) - set_property( TARGET inputbox PROPERTY CXX_STANDARD 14 ) - target_link_libraries(inputbox ${PROJECT_NAME}) - - add_executable(label_listener ../nana-demo/Examples/label_listener.cpp) - set_property( TARGET label_listener PROPERTY CXX_STANDARD 14 ) - target_link_libraries(label_listener ${PROJECT_NAME}) - - add_executable(lambda_event.Cpp11 ../nana-demo/Examples/lambda_event.Cpp11.cpp) - set_property( TARGET lambda_event.Cpp11 PROPERTY CXX_STANDARD 14 ) - target_link_libraries(lambda_event.Cpp11 ${PROJECT_NAME}) - - if (ENABLE_AUDIO) - add_executable(audio_player ../nana-demo/Examples/audio_player.cpp) - set_property( TARGET audio_player PROPERTY CXX_STANDARD 14 ) - target_link_libraries(audio_player ${PROJECT_NAME} ) - endif(ENABLE_AUDIO) - - # TODO: make it automatic to select each demo and example and build each. - #set(NANA_DEMOS_DIR ${CMAKE_SOURCE_DIR}/../nana-demo) - #set(NANA_EXAMPLES_DIR ${CMAKE_SOURCE_DIR}/../Examples/nana-demo/) - # https://cmake.org/cmake/help/v3.3/command/file.html?highlight=glob#file - #file( GLOB_RECURSE DEMO_SOURCES RELATIVE ../nana-demo *.cpp ) - - #foreach( demofile ${DEMO_SOURCES} ) - # string( REPLACE ".cpp" "" demoname ${demofile} ) - # add_executable( ${demoname} ${demofile} ) - # set_property( TARGET ${demoname} PROPERTY CXX_STANDARD 14 ) - # target_link_libraries(${demoname} ${PROJECT_NAME} )# X11 Xft ${NANA_JPEG_LIB} ${NANA_PNG_LIB}) - #endforeach( demofile ${DEMO_SOURCES} ) - -endif(BUILD_NANA_DEMOS) - - -# set compile flags -if(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") - if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11 -Wall") - else("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall") - endif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") - -endif(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") - - # enable static linkage - if (CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" AND NOT MINGW) - #set(CMAKE_FIND_LIBRARY_SUFFIXES ".a") - set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++ -pthread") - - endif () - - if (APPLE AND "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") - set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libstdc++") - endif () - +# Just for information: message ( "CMAKE_CXX_COMPILER_ID = " ${CMAKE_CXX_COMPILER_ID}) message ( "COMPILER_IS_CLANG = " ${COMPILER_IS_CLANG}) -message ( "CMAKE_CXX_FLAGS = " ${CMAKE_CXX_FLAGS}) 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 ( "ENABLE_AUDIO = " ${ENABLE_AUDIO}) +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_FIND_BOOST_FILESYSTEM = " ${NANA_CMAKE_FIND_BOOST_FILESYSTEM}) +message ( "NANA_CMAKE_BOOST_FILESYSTEM_FORCE = " ${NANA_CMAKE_BOOST_FILESYSTEM_FORCE}) +message ( "NANA_CMAKE_BOOST_FILESYSTEM_INCLUDE_ROOT = " ${NANA_CMAKE_BOOST_FILESYSTEM_INCLUDE_ROOT}) +message ( "NANA_CMAKE_BOOST_FILESYSTEM_LIB = " ${NANA_CMAKE_BOOST_FILESYSTEM_LIB}) +message ( "NANA_CMAKE_AUTOMATIC_GUI_TESTING = " ${NANA_CMAKE_AUTOMATIC_GUI_TESTING}) +message ( "NANA_CMAKE_ADD_DEF_AUTOMATIC_GUI_TESTING = " ${NANA_CMAKE_ADD_DEF_AUTOMATIC_GUI_TESTING}) + + diff --git a/README.md b/README.md index 5b985907..a8b4aec5 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ # Nana C++ Library -[Linux (gcc 5, including demos) ![TravisCI build status](https://travis-ci.org/cnjinhao/nana.svg)](https://travis-ci.org/cnjinhao/nana) +[Linux (gcc 5.4.0 and 4.9.2)![TravisCI build status](https://travis-ci.org/cnjinhao/nana.svg)](https://travis-ci.org/cnjinhao/nana) including [(nana-demos)](https://github.com/qPCR4vir/nana-demo) -[Windows (VC2015) ![AppVeyor uild status](https://ci.appveyor.com/api/projects/status/5j79p9fi887usv7h?svg=true)](https://ci.appveyor.com/project/qPCR4vir/nana) +[Windows (Microsoft (R) Build Engine version 14.0.24720.0) ![AppVeyor build status](https://ci.appveyor.com/api/projects/status/5j79p9fi887usv7h?svg=true)](https://ci.appveyor.com/project/qPCR4vir/nana) [![Licence](https://img.shields.io/badge/license-BSL-blue.svg?style=flat)](LICENSE_1_0.txt) -Nana is a C++ library designed to allow developers to easily create cross-platform GUI applications with modern C++11 style, currently it can work on Linux(X11) and Windows. The nana repository contains the entire source of library, you can browse the source code and submit your pull request for contributing. +Nana is a C++ library designed to allow developers to easily create cross-platform GUI applications with modern C++11 style. Currently it can work on Linux(X11) and Windows. The [nana repository](https://github.com/cnjinhao/nana) contains the entire source of the library. You can browse the source code and submit your pull request for contributing. ## License @@ -24,7 +24,7 @@ The best way to get help with Nana library is by visiting http://nanapro.org/hel ## Sending a Pull Request ? -This project is encourage you to contribute it through sending a pull request! There is a simple rule, please **don't** directly commit your contributions to the **master** branch. According to your commits, please choose the **hotfixes** branch or the **develop** branch. Thank you! +This project encourage you to contribute through sending a pull request! There is a simple rule: please **don't** directly commit your contributions to the **master** branch. According to your commits, please choose the **hotfixes** branch or the **develop** branch. Thank you! ## Introduction to the Repository diff --git a/build/makefile/makefile b/build/makefile/makefile index db31cf6e..9f0fb052 100644 --- a/build/makefile/makefile +++ b/build/makefile/makefile @@ -1,43 +1,53 @@ #Nana C++ Library # -#Makefile created by Jinhao(cnjinhao@hotmail.com) +#Makefile created by sarrow104(sarrow104@gmail.com) GCC = g++ INCROOT = ../../include SRCROOT = ../../source EXTRLIB = ../../extrlib NANA_INC= $(INCROOT)/nana +OUTROOT = out +#CXXFLAGS= -g -std=c++11 -Wall +CXXFLAGS= -g -fexceptions -std=c++11 -Wall -Wextra -Wunused-variable -Wfatal-errors INCS = -I$(INCROOT) -I/usr/include/freetype2 -I$(EXTRLIB) BIN = libnana.a -SRC_NANA = $(wildcard $(SRCROOT)/*.cpp) -SRC_DETAIL = $(wildcard $(SRCROOT)/detail/*.cpp) -SRC_FILESYSTEM = $(wildcard $(SRCROOT)/filesystem/*.cpp) -SRC_AUDIO = $(wildcard $(SRCROOT)/audio/*.cpp) -SRC_AUDIO_DETAIL = $(wildcard $(SRCROOT)/audio/detail/*.cpp) -SRC_GUI = $(wildcard $(SRCROOT)/gui/*.cpp) -SRC_GUI_DETAIL = $(wildcard $(SRCROOT)/gui/detail/*.cpp) -SRC_GUI_WIDGETS = $(wildcard $(SRCROOT)/gui/widgets/*.cpp) -SRC_GUI_WIDGETS_SKELETONS = $(wildcard $(SRCROOT)/gui/widgets/skeletons/*.cpp) -SRC_PAINT = $(wildcard $(SRCROOT)/paint/*.cpp) -SRC_PAINT_DETAIL = $(wildcard $(SRCROOT)/paint/detail/*.cpp) -SRC_SYSTEM = $(wildcard $(SRCROOT)/system/*.cpp) -SRC_THREADS= $(wildcard $(SRCROOT)/threads/*.cpp) +TARGET = ../bin/$(BIN) -SOURCES = $(SRC_NANA) $(SRC_DETAIL) $(SRC_FILESYSTEM) $(SRC_AUDIO) $(SRC_AUDIO_DETAIL) $(SRC_GUI) $(SRC_GUI_DETAIL) $(SRC_GUI_WIDGETS) $(SRC_GUI_WIDGETS_SKELETONS) $(SRC_PAINT) $(SRC_PAINT_DETAIL) $(SRC_SYSTEM) $(SRC_THREADS) +.PHONY: all clean install print -LINKOBJ = $(SOURCES:.cpp=.o) +all: $(TARGET) -$(BIN): $(LINKOBJ) - ar r ../bin/$(BIN) $(LINKOBJ) - ranlib ../bin/$(BIN) +define walk +$(wildcard $(1)) $(foreach e, $(wildcard $(1)/*), $(call walk, $(e))) +endef -.cpp.o: - $(GCC) -g -c $< -o $@ $(INCS) -std=c++11 -Wall +SRCFILES := $(filter %.cpp,$(patsubst ./%,%,$(filter-out .,$(call walk, $(SRCROOT))))) + +LINKOBJ = $(patsubst $(SRCROOT)/%.cpp,$(OUTROOT)/objs/%.o,$(SRCFILES)) + +print: + @echo $(LINKOBJ) + +$(TARGET): $(LINKOBJ) + mkdir -p $(dir $@) + ar rus $@ $? + ranlib $@ + +$(OUTROOT)/objs/%.o: $(SRCROOT)/%.cpp + @mkdir -p $(dir $@) + $(GCC) -o $@ -c $< $(INCS) $(CXXFLAGS) clean: rm -f $(LINKOBJ) - rm -f ../bin/$(BIN) + rm -f $(TARGET) +install: + @mkdir -p $(INSTALL_PREFIX)/include + @mkdir -p $(INSTALL_PREFIX)/bin + @mkdir -p $(INSTALL_PREFIX)/lib + cp -rfl $(INCROOT)/* $(INSTALL_PREFIX)/include + cp -rfl $(TARGET) $(INSTALL_PREFIX)/lib/ diff --git a/build/makefile/readme.md b/build/makefile/readme.md index 1b8d9b1b..38ce2141 100644 --- a/build/makefile/readme.md +++ b/build/makefile/readme.md @@ -1,8 +1,8 @@ -Building Nana C++ Library -requires: +# Building Nana C++ Library directly with make +If you are using make directly, it require: X11, pthread, Xpm, rt, dl, freetype2, Xft, fontconfig, ALSA -Writing a makefile for creating applications with Nana C++ Library +Example of writing a makefile for creating applications with Nana C++ Library ------------------- ``` GCC = g++ diff --git a/build/vc2015/nana.vcxproj.filters b/build/vc2015/nana.vcxproj.filters index 0f547b4e..4fcfbb6b 100644 --- a/build/vc2015/nana.vcxproj.filters +++ b/build/vc2015/nana.vcxproj.filters @@ -49,6 +49,9 @@ {53feb93f-2b86-4bf5-b2f3-f60ef1bbbf76} + + {6caffbf6-c023-4dbf-ba69-cdb49feddb5d} + diff --git a/extrlib/readme.txt b/extrlib/readme.txt index 08aff887..ff52e8a3 100644 --- a/extrlib/readme.txt +++ b/extrlib/readme.txt @@ -1,5 +1,12 @@ You can download the precompiled external libraries at http://sourceforge.net/projects/nanapro/files/extrlib/ Extract the ZIP file to the directory nana/extrlib. Then modify the marco switch defined in nana/include/config.hpp header file and rebuild the nana library. +A method to configure the 3rd party libraries +https://github.com/cnjinhao/nana/wiki/Configuration-of-Third-Party-Libraries-for-Nana + + 您可以下载预先编译的外部程序库,下载地址http://sourceforge.net/projects/nanapro/files/extrlib/ -将ZIP文件释放到nana/extrlib目录。然后修改nana/include/config.hpp文件中对应的标志,重新编译Nana库即可。 \ No newline at end of file +将ZIP文件释放到nana/extrlib目录。然后修改nana/include/config.hpp文件中对应的标志,重新编译Nana库即可。 + +配置第三方库的方法 +https://github.com/cnjinhao/nana/wiki/Configuration-of-Third-Party-Libraries-for-Nana \ No newline at end of file diff --git a/include/nana/any.hpp b/include/nana/any.hpp index 491169fc..ed711146 100644 --- a/include/nana/any.hpp +++ b/include/nana/any.hpp @@ -150,7 +150,7 @@ namespace nana if (!operand) return nullptr; - auto holder = dynamic_cast*>(operand->content_); + auto holder = dynamic_cast::type>*>(operand->content_); return (holder ? &holder->value : nullptr); } diff --git a/include/nana/audio/player.hpp b/include/nana/audio/player.hpp index c4fc3d87..0dad445f 100644 --- a/include/nana/audio/player.hpp +++ b/include/nana/audio/player.hpp @@ -1,6 +1,6 @@ #ifndef NANA_AUDIO_PLAYER_HPP #define NANA_AUDIO_PLAYER_HPP - +#include #include #ifdef NANA_ENABLE_AUDIO @@ -31,4 +31,7 @@ namespace nana{ namespace audio }//end namespace nana #endif //NANA_ENABLE_AUDIO -#endif \ No newline at end of file + +#include + +#endif diff --git a/include/nana/basic_types.hpp b/include/nana/basic_types.hpp index e990a681..6ddb6912 100644 --- a/include/nana/basic_types.hpp +++ b/include/nana/basic_types.hpp @@ -15,6 +15,7 @@ #include #include +#include namespace nana { @@ -83,7 +84,7 @@ namespace nana enum class mouse_action { - begin, normal = begin, over, pressed, end + begin, normal = begin, hovered, pressed, end }; enum class element_state @@ -336,67 +337,100 @@ namespace nana bool operator==(const color& other) const; bool operator!=(const color& other) const; + + friend color operator+(const color&, const color&); private: - double r_; - double g_; - double b_; + double r_{ 0.0 }; + double g_{ 0.0 }; + double b_{ 0.0 }; double a_{ 0.0 }; //invisible }; - - struct rectangle; - - struct point + template + struct basic_point { - point(); - point(int x, int y); - point(const rectangle&); + //typedef-names + using value_type = T; - point& operator=(const rectangle&); - bool operator==(const point&) const; - bool operator!=(const point&) const; - bool operator<(const point&) const; - bool operator<=(const point&) const; - bool operator>(const point&) const; - bool operator>=(const point&) const; + //data member + value_type x{}; + value_type y{}; - point operator-(const point&) const; - point operator+(const point&) const; - point& operator-=(const point&); - point& operator+=(const point&); + //member functions + basic_point() = default; - int x; - int y; + basic_point(value_type x, value_type y) + : x{ x }, y{y} + {} + + bool operator==(const basic_point& other) const noexcept + { + return (x == other.x && y == other.y); + } + + bool operator!=(const basic_point& other) const noexcept + { + return (x != other.x || y != other.y); + } + + bool operator<(const basic_point& other) const noexcept + { + return ((y < other.y) || (y == other.y && x < other.x)); + } + + bool operator<=(const basic_point& other) const noexcept + { + return ((y < other.y) || (y == other.y && x <= other.x)); + } + + bool operator>(const basic_point& other) const noexcept + { + return ((y > other.y) || (y == other.y && x > other.x)); + } + + bool operator>=(const basic_point& other) const noexcept + { + return ((y > other.y) || (y == other.y && x >= other.x)); + } + + basic_point operator-(const basic_point& other) const noexcept + { + return{ x - other.x, y - other.y }; + } + + basic_point operator+(const basic_point& other) const noexcept + { + return{ x + other.x, y + other.y }; + } + + basic_point& operator-=(const basic_point& other) noexcept + { + x -= other.x; + y -= other.y; + return *this; + } + + basic_point& operator+=(const basic_point& other) noexcept + { + x += other.x; + y += other.y; + return *this; + } }; - struct upoint - { - typedef unsigned value_type; - - upoint(); - upoint(value_type x, value_type y); - bool operator==(const upoint&) const; - bool operator!=(const upoint&) const; - bool operator<(const upoint&) const; - bool operator<=(const upoint&) const; - bool operator>(const upoint&) const; - bool operator>=(const upoint&) const; - - value_type x; - value_type y; - }; + using point = basic_point; + using upoint = basic_point; struct size { using value_type = unsigned; size(); size(value_type width, value_type height); - size(const rectangle&); - - size& operator=(const rectangle&); bool empty() const; ///< true if width * height == 0 bool is_hit(const point&) const; ///< Assume it is a rectangle at (0,0), and check whether a specified position is in the rectange. + size& shift(); + bool operator==(const size& rhs) const; bool operator!=(const size& rhs) const; size operator+(const size&) const; @@ -415,11 +449,11 @@ namespace nana bool operator==(const rectangle& rhs) const; bool operator!=(const rectangle& rhs) const; - rectangle& operator=(const point&); - rectangle& operator=(const size&); + point position() const noexcept; + rectangle& position(const point&) noexcept; - rectangle& set_pos(const point&); - rectangle& set_size(const size&); + size dimension() const noexcept; + rectangle& dimension(const size&) noexcept; rectangle& pare_off(int pixels); /// #endif diff --git a/include/nana/c++defines.hpp b/include/nana/c++defines.hpp index d23e0a48..bd39a822 100644 --- a/include/nana/c++defines.hpp +++ b/include/nana/c++defines.hpp @@ -31,10 +31,14 @@ * - _SCL_SECURE_NO_WARNNGS, _CRT_SECURE_NO_DEPRECATE (VC) * - STD_CODECVT_NOT_SUPPORTED (VC RC, is a known issue on libstdc++, it works on libc++) * - STD_THREAD_NOT_SUPPORTED (GCC < 4.8.1) + * - STD_put_time_NOT_SUPPORTED (GCC < 5) * - STD_NUMERIC_CONVERSIONS_NOT_SUPPORTED (MinGW with GCC < 4.8.1) * - STD_NUMERIC_CONVERSIONS_NOT_SUPPORTED (MinGW with GCC < 4.8.1) * - STD_TO_STRING_NOT_SUPPORTED (MinGW with GCC < 4.8) - */ + * - STD_FILESYSTEM_NOT_SUPPORTED (GCC < 5.3) .... + * - CXX_NO_INLINE_NAMESPACE (Visual C++ < 2015) + * - STD_MAKE_UNIQUE_NOT_SUPPORTED (GCC < 4.9) + */ #ifndef NANA_CXX_DEFINES_INCLUDED #define NANA_CXX_DEFINES_INCLUDED @@ -42,14 +46,20 @@ //C++ language #if defined(_MSC_VER) -# undef STD_FILESYSTEM_NOT_SUPPORTED # if (_MSC_VER < 1900) +# //About std.experimental.filesystem. +# //Through VC2013 has provided , but all the names are given in namespace std. It's hard to alias these names into std::experimental, +# //So Nana use nana.filesystem implement instead for VC2013 +# # //Nana defines some macros for lack of support of keywords # define _ALLOW_KEYWORD_MACROS # # define CXX_NO_INLINE_NAMESPACE //no support of C++11 inline namespace until Visual C++ 2015 # define noexcept //no support of noexcept until Visual C++ 2015 -# define constexpr //no support of constexpr until Visual C++ 2015 + +# define constexpr const //no support of constexpr until Visual C++ 2015 ? const ?? +# else +# undef STD_FILESYSTEM_NOT_SUPPORTED # endif #elif defined(__GNUC__) # if (__GNUC__ == 4 && __GNUC_MINOR__ < 6) @@ -76,7 +86,7 @@ #define NANA_LINUX #define NANA_X11 #else -# static_assert(false, "Only Windows and Unix are supported now (Mac OS is experimental)"); + static_assert(false, "Only Windows and Unix are supported now (Mac OS is experimental)"); #endif //Define a symbol for POSIX operating system. @@ -108,9 +118,10 @@ // is a known issue on libstdc++, it works on libc++ #define STD_CODECVT_NOT_SUPPORTED - - #ifndef STD_MAKE_UNIQUE_NOT_SUPPORTED - #define STD_MAKE_UNIQUE_NOT_SUPPORTED + #if !defined(__cpp_lib_make_unique) || (__cpp_lib_make_unique != 201304) + #ifndef STD_MAKE_UNIQUE_NOT_SUPPORTED + #define STD_MAKE_UNIQUE_NOT_SUPPORTED + #endif #endif #endif @@ -133,9 +144,15 @@ #endif #endif - #if ((__GNUC__ > 5) || ((__GNUC__ == 5) && (__GNUC_MINOR__ >= 3 ) ) ) - #undef STD_FILESYSTEM_NOT_SUPPORTED - #endif + + #if ((__GNUC__ < 5) ) + # define STD_put_time_NOT_SUPPORTED + #endif + + #if ((__GNUC__ > 5) || ((__GNUC__ == 5) && (__GNUC_MINOR__ >= 3 ) ) ) + # undef STD_FILESYSTEM_NOT_SUPPORTED + /// \todo define the namespace ???? + #endif #if (__GNUC__ == 4) #if ((__GNUC_MINOR__ < 8) || (__GNUC_MINOR__ == 8 && __GNUC_PATCHLEVEL__ < 1)) @@ -147,8 +164,10 @@ #endif #if defined(NANA_MINGW) - //It's a knonwn issue under MinGW - #define STD_NUMERIC_CONVERSIONS_NOT_SUPPORTED + #ifndef __MINGW64_VERSION_MAJOR + //It's a knonwn issue under MinGW(except MinGW-W64) + #define STD_NUMERIC_CONVERSIONS_NOT_SUPPORTED + #endif #endif #if (__GNUC_MINOR__ < 8) @@ -177,9 +196,9 @@ // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0061r0.html -# if __cpp_lib_experimental_filesystem -# undef STD_FILESYSTEM_NOT_SUPPORTED -# endif +#if defined(__cpp_lib_experimental_filesystem) && (__cpp_lib_experimental_filesystem == 201406) +# undef STD_FILESYSTEM_NOT_SUPPORTED +#endif #ifdef __has_include diff --git a/include/nana/charset.hpp b/include/nana/charset.hpp index 41fa4d72..09f79bdf 100644 --- a/include/nana/charset.hpp +++ b/include/nana/charset.hpp @@ -46,7 +46,31 @@ namespace nana class charset_encoding_interface; } - /// An intelligent charset class for character code conversion. + /*!\class charset + \brief An intelligent charset class for character code conversion. + Example: + 1. A UTF-8 string from the socket. + + int len = ::recv(sd, buf, buflen, 0); + textbox.caption(nana::charset(std::string(buf, len), nana::unicode::utf8)); + + 2. Send the string in text to the socket as UTF-8. + + std::string utf8str = nana::charset(textbox.caption()).to_bytes(nana::unicode::utf8); + ::send(sd, utf8str.c_str(), utf8str.size(), 0); + + 3, Convert a string to the specified multi-byte character code. + + // Convert to a multibytes string through default system language. + std::string mbstr = nana::charset(a_wstring); + + // If the default system language is English and convert + // a Chinese unicode string to multibytes string through GB2312 + std::setlocale(LC_CTYPE, "zh_CN.GB2312"); + //set::setlocale(LC_CTYPE, ".936"); call it in Windows + std::string mbstr = nana::charset(a_wstring_with_chinese); + + */ class charset { public: @@ -74,27 +98,3 @@ namespace nana }//end namespace nana #endif -/*!\class charset - -Example -1. A UTF-8 string from the socket. - - int len = ::recv(sd, buf, buflen, 0); - textbox.caption(nana::charset(std::string(buf, len), nana::unicode::utf8)); - -2. Send the string in text to the socket as UTF-8. - - std::string utf8str = nana::charset(textbox.caption()).to_bytes(nana::unicode::utf8); - ::send(sd, utf8str.c_str(), utf8str.size(), 0); - -3, Convert a string to the specified multi-byte character code. - - //Convert to a multibytes string through default system language. - std::string mbstr = nana::charset(a_wstring); - //If the default system language is English and convert - //a Chinese unicode string to multibytes string through GB2312 - std::setlocale(LC_CTYPE, "zh_CN.GB2312"); - //set::setlocale(LC_CTYPE, ".936"); call it in Windows - std::string mbstr = nana::charset(a_wstring_with_chinese); - -*/ \ No newline at end of file diff --git a/include/nana/config.hpp b/include/nana/config.hpp index 6335f2e2..17c5cdc2 100644 --- a/include/nana/config.hpp +++ b/include/nana/config.hpp @@ -25,7 +25,11 @@ #include "c++defines.hpp" -//The basic configurations are ignored when NANA_IGNORE_CONF is defined. +//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 @@ -39,20 +43,30 @@ // https://github.com/meganz/mingw-std-threads //#define NANA_ENABLE_MINGW_STD_THREADS_WITH_MEGANZ -//////////////////////////// -// The ISO C++ File System Technical Specification 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 -// The selected option will be set by nana into std::experimental::filesystem -// By default Nana will use the ISO TS if available, or nana if not. -// Boost will be use only if you change one of the following (set the includes and link correspondly): -//#define NANA_BOOST_FILESYSTEM_AVAILABLE // "Is Boost filesystem available?" -//#define NANA_BOOST_FILESYSTEM_PREFERRED // "Is Boost filesystem preferred over nana?" -//#define NANA_BOOST_FILESYSTEM_FORCE // "Force use of Boost filesystem if available (over ISO)? +//# 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 +//# the selected option will be set by nana into std::experimental::filesystem +//# By default Nana will try to use the STD.If not available will try +//# to use boost if available.Nana own implementation will be use only none of them are 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): +// +//#define BOOST_FILESYSTEM_AVAILABLE // "Is Boost filesystem available?" +//#define BOOST_FILESYSTEM_FORCE // "Force use of Boost filesystem if available (over ISO and nana) +//#define STD_FILESYSTEM_FORCE // "Use of STD filesystem?(a compilation error will ocurre if not available)" OFF) +//#define NANA_FILESYSTEM_FORCE // "Force nana filesystem over ISO and boost?" OFF) +// +// Make sure you (cmake?) provide the following where correspond (please find the correct values): +// set CMAKE_BOOST_FILESYSTEM_INCLUDE_ROOT "Where to find ?" "../") +// set CMAKE_BOOST_FILESYSTEM_LIB "Flag for the compiler to link: " "-lboost/fs") +// include_directories CMAKE_BOOST_FILESYSTEM_INCLUDE_ROOT +// APPEND flag LINKS CMAKE_BOOST_FILESYSTEM_LIB + /////////////////// // Support of PCM playback @@ -83,12 +97,22 @@ #endif #endif +/////////////////// +// Support for NANA_AUTOMATIC_GUI_TESTING +// Will cause the program to self-test the GUI. A default automatic GUI test +// will be added to all programs which don't have yet one defined. This default test will simple +// wait 10 sec. (time to construct, show and execute the GUI) and then exit normally. +// +//#define NANA_AUTOMATIC_GUI_TESTING + + + #if !defined(VERBOSE_PREPROCESSOR) //#define VERBOSE_PREPROCESSOR #endif #if !defined(STOP_VERBOSE_PREPROCESSOR) -#define STOP_VERBOSE_PREPROCESSOR +//#define STOP_VERBOSE_PREPROCESSOR #endif #endif // NANA_IGNORE_CONFIG diff --git a/include/nana/deploy.hpp b/include/nana/deploy.hpp index 35659972..82025904 100644 --- a/include/nana/deploy.hpp +++ b/include/nana/deploy.hpp @@ -14,11 +14,10 @@ #ifndef NANA_DEPLOY_HPP #define NANA_DEPLOY_HPP +#include #include -#if defined(VERBOSE_PREPROCESSOR) - #include -#endif + #include #include @@ -92,14 +91,57 @@ namespace std } #endif +#ifdef STD_put_time_NOT_SUPPORTED +#include +namespace std +{ + //Workaround for no implemenation of std::put_time in gcc < 5. + /* std unspecified return type */ + //template< class CharT, class RTSTR >// let fail for CharT != char / wchar_t + //RTSTR put_time(const std::tm* tmb, const CharT* fmt); + + //template< > + std::string put_time/**/(const std::tm* tmb, const char* fmt); + + //Defined in header + // std::size_t strftime(char* str, std::size_t count, const char* format, const std::tm* time); + //template<> + //std::wstring put_time(const std::tm* tmb, const wchar_t* fmt); +} +#endif // STD_put_time_NOT_SUPPORTED + namespace nana { + /// move to *.h ?? + struct utf8_Error : std::runtime_error + { + static bool use_throw; ///< def { true }; use carefully - it is a global variable !! \todo initialize from a #define ? + + using std::runtime_error::runtime_error; + +#if defined(_MSC_VER) +# if (_MSC_VER < 1900) + //A workaround for lack support of C++11 inheriting constructors for VC2013 + explicit utf8_Error(const std::string& msg); +# endif +#endif + + void emit(); + }; + + /// Checks whether a specified text is utf8 encoding - bool is_utf8(const char* str, unsigned len); + bool is_utf8(const char* str, std::size_t len); void throw_not_utf8(const std::string& text); - void throw_not_utf8(const char*, unsigned len); + void throw_not_utf8(const char*, std::size_t len); void throw_not_utf8(const char*); + /// this text needed change, it needed review ?? + bool review_utf8(const std::string& text); + + /// this text needed change, it needed review ?? + bool review_utf8(std::string& text); + const std::string& to_utf8(const std::string&); std::string to_utf8(const std::wstring&); @@ -190,5 +232,5 @@ namespace std { make_unique(Args&&...) = delete; } #endif //STD_make_unique_NOT_SUPPORTED - -#endif //NANA_MACROS_HPP +#include +#endif //NANA_DEPLOY_HPP diff --git a/include/nana/detail/linux_X11/platform_spec.hpp b/include/nana/detail/linux_X11/platform_spec.hpp index c8866d9d..e701c203 100644 --- a/include/nana/detail/linux_X11/platform_spec.hpp +++ b/include/nana/detail/linux_X11/platform_spec.hpp @@ -18,6 +18,8 @@ #ifndef NANA_DETAIL_PLATFORM_SPEC_HPP #define NANA_DETAIL_PLATFORM_SPEC_HPP +#include + #include #include #include @@ -60,6 +62,8 @@ namespace detail class charset_conv { + charset_conv(const charset_conv&) = delete; + charset_conv& operator=(const charset_conv*) = delete; public: charset_conv(const char* tocode, const char* fromcode); ~charset_conv(); @@ -118,6 +122,9 @@ namespace detail void update_color(); void update_text_color(); private: + drawable_impl_type(const drawable_impl_type&) = delete; + drawable_impl_type& operator=(const drawable_impl_type&) = delete; + unsigned current_color_{ 0xFFFFFF }; unsigned color_{ 0xFFFFFFFF }; unsigned text_color_{ 0xFFFFFFFF }; @@ -188,8 +195,8 @@ namespace detail native_window_type owner; std::vector * owned; }; - public: - int error_code; + public: + int error_code; public: typedef drawable_impl_type::font_ptr_t font_ptr_t; typedef void (*timer_proc_type)(unsigned tid); @@ -197,6 +204,8 @@ namespace detail typedef ::nana::event_code event_code; typedef ::nana::native_window_type native_window_type; + platform_spec(const platform_spec&) = delete; + platform_spec& operator=(const platform_spec&) = delete; platform_spec(); ~platform_spec(); @@ -327,6 +336,7 @@ namespace detail }//end namespace nana +#include // .h ward #endif diff --git a/include/nana/detail/platform_spec_selector.hpp b/include/nana/detail/platform_spec_selector.hpp index d230088f..fc4504f8 100644 --- a/include/nana/detail/platform_spec_selector.hpp +++ b/include/nana/detail/platform_spec_selector.hpp @@ -1,15 +1,15 @@ -/* +/** * Selector of Platform Specification * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) * - * @file: nana/detail/platform_spec_selector.hpp + * @file nana/detail/platform_spec_selector.hpp * - * Selects the proper platform_spec header file for the current platform + * @brief Selects the proper platform_spec header file for the current platform */ #include diff --git a/include/nana/detail/win32/platform_spec.hpp b/include/nana/detail/win32/platform_spec.hpp index dc6329fe..23abd002 100644 --- a/include/nana/detail/win32/platform_spec.hpp +++ b/include/nana/detail/win32/platform_spec.hpp @@ -1,7 +1,7 @@ /* * Platform Specification Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -25,6 +25,7 @@ #include #include #include +#include namespace nana { @@ -63,17 +64,27 @@ namespace detail bool forced; }; + struct arg_affinity_execute + { + const std::function * function_ptr; + }; + enum { tray = 0x501, + async_activate, async_set_focus, - map_thread_root_buffer, + remote_flush_surface, remote_thread_destroy_window, remote_thread_move_window, operate_caret, //wParam: 1=Destroy, 2=SetPos remote_thread_set_window_pos, remote_thread_set_window_text, + + //Execute a function in a thread with is associated with a specified native window + affinity_execute, + user, }; }; @@ -142,6 +153,9 @@ namespace detail unsigned whitespace_pixels; }string; + drawable_impl_type(const drawable_impl_type&) = delete; + drawable_impl_type& operator=(const drawable_impl_type&) = delete; + drawable_impl_type(); ~drawable_impl_type(); diff --git a/include/nana/filesystem/filesystem.hpp b/include/nana/filesystem/filesystem.hpp index a3a3e373..836b7778 100644 --- a/include/nana/filesystem/filesystem.hpp +++ b/include/nana/filesystem/filesystem.hpp @@ -1,4 +1,4 @@ -/* +/** * A ISO C++ filesystem Implementation * Nana C++ Library(http://www.nanapro.org) * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) @@ -7,17 +7,17 @@ * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) * - * @file: nana/filesystem/filesystem.hpp - * Modiffied by Ariel Vina-Rodriguez: - * Now mimic std::experimental::filesystem::v1 (boost v3) - * and need VC2015 or a C++11 compiler. With a few correction will be compiler by VC2013 + * @file nana/filesystem/filesystem.hpp + * @author Ariel Vina-Rodriguez, Jinhao + * @brief Mimic std::experimental::filesystem::v1 (boost v3) + * 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://en.cppreference.com/w/cpp/experimental/fs // http://cpprocks.com/introduction-to-tr2-filesystem-library-in-vs2012/ --- TR2 filesystem in VS2012 // https://msdn.microsoft.com/en-us/library/hh874694%28v=vs.140%29.aspx --- C++ 14, the header VS2015 // https://msdn.microsoft.com/en-us/library/hh874694%28v=vs.120%29.aspx --- header VS2013 -// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4100.pdf --- last pdf of std draft N4100 2014-07-04 // http://cplusplus.github.io/filesystem-ts/working-draft.html --- in html format // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4099.html --- in html format // http://article.gmane.org/gmane.comp.lib.boost.devel/256220 --- The filesystem TS unanimously approved by ISO. @@ -29,6 +29,54 @@ #ifndef NANA_FILESYSTEM_HPP #define NANA_FILESYSTEM_HPP +#include + +//Filesystem Selection +#include + +#if defined(NANA_USING_NANA_FILESYSTEM) || defined(NANA_USING_STD_FILESYSTEM) || defined(NANA_USING_BOOST_FILESYSTEM) +#undef NANA_USING_NANA_FILESYSTEM +#undef NANA_USING_STD_FILESYSTEM +#undef NANA_USING_BOOST_FILESYSTEM +#endif + +#define NANA_USING_NANA_FILESYSTEM 0 +#define NANA_USING_STD_FILESYSTEM 0 +#define NANA_USING_BOOST_FILESYSTEM 0 + +#if (defined(NANA_FILESYSTEM_FORCE) || ( (defined(STD_FILESYSTEM_NOT_SUPPORTED) && !defined(BOOST_FILESYSTEM_AVAILABLE)) && !(defined(BOOST_FILESYSTEM_FORCE) || defined(STD_FILESYSTEM_FORCE)) ) ) + +#undef NANA_USING_NANA_FILESYSTEM +#define NANA_USING_NANA_FILESYSTEM 1 + +#elif (defined(BOOST_FILESYSTEM_AVAILABLE) && ( defined(BOOST_FILESYSTEM_FORCE) || ( defined(STD_FILESYSTEM_NOT_SUPPORTED) && !defined(STD_FILESYSTEM_FORCE) ) )) + +#undef NANA_USING_BOOST_FILESYSTEM +#define NANA_USING_BOOST_FILESYSTEM 1 +# include + +// add boost::filesystem into std::experimental::filesystem +namespace std { + namespace experimental { + namespace filesystem { + using namespace boost::filesystem; + } // filesystem + } // experimental +} // std + +#else + +#undef NANA_USING_STD_FILESYSTEM +#define NANA_USING_STD_FILESYSTEM 1 +# include +#endif + +#ifndef __cpp_lib_experimental_filesystem +# define __cpp_lib_experimental_filesystem 1 +#endif + +#if NANA_USING_NANA_FILESYSTEM + #include #include #include @@ -40,18 +88,14 @@ #include - // namespace std { namespace experimental { namespace filesystem { inline namespace v1 { - -namespace nana { namespace experimental { - -#ifndef CXX_NO_INLINE_NAMESPACE -inline namespace v1 +namespace nana { namespace experimental { namespace filesystem { +#ifndef CXX_NO_INLINE_NAMESPACE + inline namespace v1 + { #endif -namespace filesystem -{ - enum class file_type + enum class file_type { none = 0, ///< has not been determined or an error occurred while trying to determine not_found = -1, ///< Pseudo-type: file was not found. Is not considered an error @@ -82,7 +126,7 @@ namespace filesystem uintmax_t available; }; - using file_time_type = std::chrono::time_point;// trivial-clock> ; + using file_time_type = std::chrono::time_point; ///< trivial-clock> ; class file_status { @@ -116,7 +160,7 @@ namespace filesystem public: #if defined(NANA_WINDOWS) using value_type = wchar_t; - const static value_type preferred_separator = '\\'; + const static value_type preferred_separator = L'\\'; #else using value_type = char; const static value_type preferred_separator = '/'; @@ -131,23 +175,41 @@ namespace filesystem _m_assign(source); } + // modifiers + //void clear() noexcept; + path& make_preferred(); + path& remove_filename(); + //path& replace_filename(const path& replacement); + //path& replace_extension(const path& replacement = path()); + //void swap(path& rhs) noexcept; + + // decomposition + //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; + path extension() const; + + // 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_stem() const; + bool has_extension() const { return !extension().string().empty(); }; // temp + //bool is_absolute() const; + //bool is_relative() const; int compare(const path& other) const; - bool empty() const; - path extension() const; - - path parent_path() const; file_type what() const; - //decomposition - path filename() const; - - //modifiers - path& remove_filename(); - - - const value_type*c_str() const; const string_type& native() const; operator string_type() const; @@ -203,8 +265,9 @@ namespace filesystem filesystem_error(const std::string& msg, const path& path1, std::error_code err); filesystem_error(const std::string& msg, const path& path1, const path& path2, std::error_code err); - const path& path1() const; //noexcept - const path& path2() const; //noexcept + const path& path1() const noexcept; + const path& path2() const noexcept; + // const char* what() const noexcept; private: path path1_; path path2_; @@ -215,47 +278,37 @@ namespace filesystem { public: directory_entry() = default; - explicit directory_entry(const path&); + explicit directory_entry(const ::nana::experimental::filesystem::path&); //modifiers - void assign(const path&); - void replace_filename(const path&); + void assign(const ::nana::experimental::filesystem::path&); + void replace_filename(const ::nana::experimental::filesystem::path&); //observers file_status status() const; - operator const filesystem::path&() const; + operator const filesystem::path&() const { return path_; }; const filesystem::path& path() const; private: - filesystem::path path_; + ::nana::experimental::filesystem::path path_; }; - /// an iterator for a sequence of directory_entry elements representing the files in a directory, not an recursive_directory_iterator - //template + /// InputIterator that iterate over the sequence of directory_entry elements representing the files in a directory, not an recursive_directory_iterator class directory_iterator :public std::iterator { using find_handle = void*; public: - using value_type = directory_entry ; - typedef ptrdiff_t difference_type; - typedef const directory_entry* pointer; - typedef const directory_entry& reference; - typedef std::input_iterator_tag iterator_category; - directory_iterator(); - directory_iterator(const path& file_path); + directory_iterator() noexcept; + explicit directory_iterator(const path& dir); const value_type& operator*() const; const value_type* operator->() const; directory_iterator& operator++(); - directory_iterator operator++(int); + directory_iterator operator++(int); ///< extention - bool equal(const directory_iterator& x) const; + bool equal(const directory_iterator& x) const; - // enable directory_iterator range-based for statements - directory_iterator begin(); - directory_iterator end(); - private: template static bool _m_ignore(const Char * p) @@ -275,6 +328,16 @@ namespace filesystem find_handle handle_{nullptr}; value_type value_; }; + /// enable directory_iterator range-based for statements + inline directory_iterator begin( directory_iterator iter) noexcept + { + return iter; + } + + inline directory_iterator end( const directory_iterator&) noexcept + { + return {}; + } //class recursive_directory_iterator; @@ -301,15 +364,23 @@ namespace filesystem std::uintmax_t file_size(const path& p); //uintmax_t file_size(const path& p, error_code& ec) noexcept; - inline bool is_directory(file_status s) { return s.type() == file_type::directory ;} + inline bool is_directory(file_status s) noexcept + { return s.type() == file_type::directory ;} + bool is_directory(const path& p); - inline bool is_directory(const directory_entry& d) - { - return is_directory(d.status()); - } + //bool is_directory(const path& p, error_code& ec) noexcept; - //bool is_regular_file(file_status s) noexcept; + inline bool is_regular_file(file_status s) noexcept + { + return s.type() == file_type::regular; + } + inline bool is_regular_file(const path& p) + { + return is_regular_file(status(p)); + } + // bool is_regular_file(const path& p, error_code& ec) noexcept; + // Returns: is_regular_file(status(p, ec)).Returns false if an error occurs. inline bool is_empty(const path& p) { @@ -320,7 +391,7 @@ namespace filesystem return (file_size(p) == 0); } - //bool is_empty(const path& p, error_code& ec) noexcept; + // bool is_empty(const path& p, error_code& ec) noexcept; bool create_directories(const path& p); @@ -330,16 +401,19 @@ namespace filesystem bool create_directory(const path& p, const path& attributes); //bool create_directory(const path& p, const path& attributes, error_code& ec) noexcept; - bool modified_file_time(const path& p, struct tm&); - path path_user(); - + /// The time of last data modification of p, determined as if by the value of the POSIX + /// stat structure member st_mtime obtained as if by POSIX stat(). + file_time_type last_write_time(const path& p); + /// returns file_time_type::min() if an error occurs + //file_time_type last_write_time(const path& p, error_code& ec) noexcept; + + path current_path(); //path current_path(error_code& ec); - void current_path(const path& p); + void current_path(const path& p); ///< chdir //void current_path(const path& p, error_code& ec) noexcept; - bool remove(const path& p); bool remove(const path& p, std::error_code& ec); // noexcept; @@ -381,4 +455,21 @@ namespace filesystem //namespace filesystem = experimental::filesystem; } //end namespace nana -#endif + +namespace std { + namespace experimental { + namespace filesystem { + +# ifdef CXX_NO_INLINE_NAMESPACE + using namespace nana::experimental::filesystem; +# else + using namespace nana::experimental::filesystem::v1; +# endif + } // filesystem + } // experimental +} // std + +#endif //NANA_USING_NANA_FILESYSTEM + +#include +#endif //NANA_FILESYSTEM_HPP diff --git a/include/nana/filesystem/filesystem_ext.hpp b/include/nana/filesystem/filesystem_ext.hpp new file mode 100644 index 00000000..39fc4a07 --- /dev/null +++ b/include/nana/filesystem/filesystem_ext.hpp @@ -0,0 +1,129 @@ +/** +* Nana C++ Library(http://www.nanapro.org) +* Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) +* +* Distributed under the Boost Software License, Version 1.0. +* (See accompanying file LICENSE_1_0.txt or copy at +* http://www.boost.org/LICENSE_1_0.txt) +* +* @file nana\filesystem\filesystem_ext.hpp +* @autor by Ariel Vina-Rodriguez: +* @brief Some convenient extensions to the filesystem library. +* +*/ + +#ifndef NANA_FILESYSTEM_EXT_HPP +#define NANA_FILESYSTEM_EXT_HPP + +#include + +namespace nana +{ +namespace filesystem_ext +{ + +#if defined(NANA_WINDOWS) + constexpr auto def_root = "C:"; + constexpr auto def_rootstr = "C:\\"; + constexpr auto def_rootname = "Local Drive(C:)"; +#elif defined(NANA_LINUX) + constexpr auto def_root = "/"; + constexpr auto def_rootstr = "/"; + constexpr auto def_rootname = "Root/"; +#endif + +std::experimental::filesystem::path path_user(); ///< extention ? + +inline bool is_directory(const std::experimental::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 +{ + using directory_iterator = std::experimental::filesystem::directory_iterator; + + directory_only_iterator& find_first() + { + auto end = directory_only_iterator{}; + while (*this != end) + { + if (is_directory((**this).status())) + return *this; + this->directory_iterator::operator++(); + } + return *this; + } +public: + directory_only_iterator() = default; + + template + directory_only_iterator(Arg&& arg, Args&&... args) : directory_iterator(arg, std::forward(args)...) + { + find_first(); + } + + directory_only_iterator& operator++() + { + this->directory_iterator::operator++(); + return find_first(); + } +}; + +inline directory_only_iterator begin(directory_only_iterator iter) noexcept +{ + return iter; +} + +inline directory_only_iterator end(const directory_only_iterator&) noexcept +{ + return{}; +} + +//template // DI = directory_iterator from std, boost, or nana : value_type directory_entry +class regular_file_only_iterator : public std::experimental::filesystem::directory_iterator +{ + using directory_iterator = std::experimental::filesystem::directory_iterator; + regular_file_only_iterator& find_first() + { + while (((*this) != directory_iterator{}) && !is_regular_file((**this).status())) + this->directory_iterator::operator++(); + return (*this); + } +public: + regular_file_only_iterator() = default; + + template + regular_file_only_iterator(Arg&& arg, Args&&... args) : directory_iterator(std::forward(arg), std::forward(args)...) + { + find_first(); + } + + regular_file_only_iterator& operator++() + { + this->directory_iterator::operator++(); + return find_first(); + } +}; + +inline regular_file_only_iterator begin(regular_file_only_iterator iter) noexcept +{ + return iter; +} + +inline regular_file_only_iterator end(const regular_file_only_iterator&) noexcept +{ + return{}; +} + +std::string pretty_file_size(const std::experimental::filesystem::path& path); + +std::string pretty_file_date(const std::experimental::filesystem::path& path); + +bool modified_file_time(const std::experimental::filesystem::path& p, struct tm&); ///< extention ? + +} // filesystem_ext +} // nana + +#endif //NANA_FILESYSTEM_EXT_HPP diff --git a/include/nana/gui/animation.hpp b/include/nana/gui/animation.hpp index d9a62e26..9f0df563 100644 --- a/include/nana/gui/animation.hpp +++ b/include/nana/gui/animation.hpp @@ -12,7 +12,7 @@ #ifndef NANA_GUI_ANIMATION_HPP #define NANA_GUI_ANIMATION_HPP - +#include #include #include @@ -82,4 +82,5 @@ namespace nana impl * impl_; }; } //end namespace nana +#include #endif //NANA_GUI_ANIMATION_HPP diff --git a/include/nana/gui/basis.hpp b/include/nana/gui/basis.hpp index 26ca8885..a13b3bac 100644 --- a/include/nana/gui/basis.hpp +++ b/include/nana/gui/basis.hpp @@ -1,20 +1,22 @@ -/* +/** + * \file basis.hpp + * \brief This file provides basis class and data structures required by the GUI + * * Basis Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) * - * @file: nana/gui/basis.hpp - * - * This file provides basis class and data structrue that required by gui */ #ifndef NANA_GUI_BASIS_HPP #define NANA_GUI_BASIS_HPP +#include + #include "../basic_types.hpp" #include "../traits.hpp" //metacomp::fixed_type_set @@ -54,14 +56,18 @@ namespace nana super, widget = 0x1, lite_widget = 0x3, - root = 0x5, - frame = 0x9 + 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 : widget_tag{ static const flags value = flags::lite_widget;}; - struct root_tag : widget_tag{ static const flags value = flags::root; }; - struct frame_tag: widget_tag{ static const flags value = flags::frame; }; + 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*; @@ -95,6 +101,7 @@ namespace nana undo = substitute, //System Code for OS + os_tab = 0x09, os_shift = 0x10, os_ctrl = 0x11, os_pageup = 0x21, os_pagedown, @@ -151,7 +158,25 @@ namespace nana appearance(); appearance(bool has_decoration, bool taskbar, bool floating, bool no_activate, bool min, bool max, bool sizable); }; - /// Provided to generate an appearance object with better readability and understandability + + +/** @brief Provided to generate an appearance object with better readability and understandability + +A window has an appearance. This appearance can be specified when a window is being created. +To determine the appearance of a window there is a structure named nana::appearance with +a bool member for each feature with can be included or excluded in the "apereance" of the windows form. +But in practical development is hard to describe the style of the appearance using the struct nana::appearance. +If a form would to be defined without min/max button and sizable border, then + +\code{.CPP} + nana::form form(x, y, width, height, nana::appearance(false, false, false, true, false)); +\endcode + +This piece of code may be confusing because of the 5 parameters of the constructor of `nana::form`. So the library provides a helper class for making it easy. +For better readability and understandability Nana provides three templates classes to generate an appearance object: +nana::appear::decorate, nana::appear::bald and nana::appear::optional. Each provide an operator +that return a corresponding nana::appearance with predefined values. +*/ struct appear { struct minimize{}; @@ -160,7 +185,20 @@ namespace nana struct taskbar{}; struct floating{}; struct no_activate{}; - /// Create an appearance of a window with "decoration" + + /** @brief Create an appearance of a window with "decoration" in non-client area, such as title bar + * + * We can create a form without min/max button and sizable border like this: + * \code{.CPP} + * using nana::appear; + * nana::form form(x, y, width, height, appear::decorate()); + * \endcode + * The appearance created by appear::decorate<>() has a titlebar and borders that are draw by the + * platform- window manager. If a window needs a minimize button, it should be: + * \code{.CPP} + * appear::decorate() + * \endcode + */ template< typename Minimize = null_type, typename Maximize = null_type, typename Sizable = null_type, @@ -181,7 +219,8 @@ namespace nana ); } }; - /// Create an appearance of a window without "decoration" + + /// Create an appearance of a window without "decoration" with no titlebar and no 3D-look borders. template < typename Taskbar = null_type, typename Floating = null_type, typename NoActive = null_type, @@ -225,5 +264,39 @@ namespace nana } }; };//end namespace apper + + /// Interface for caret operations + class caret_interface + { + public: + virtual ~caret_interface() = default; + + virtual void disable_throw() noexcept = 0; + + virtual void effective_range(const rectangle& range) = 0; + + virtual void position(const point& pos) = 0; + virtual point position() const = 0; + + virtual void dimension(const size& size) = 0; + virtual size dimension() const = 0; + + virtual void visible(bool visibility) = 0; + virtual bool visible() const = 0; + };//end class caret_interface + + namespace parameters + { + /// The system-wide parameters for mouse wheel + struct mouse_wheel + { + unsigned lines; ///< The number of lines to scroll when the vertical mouse wheel is moved. + unsigned characters; ///< The number of characters to scroll when the horizontal mouse wheel is moved. + + mouse_wheel(); + }; + } }//end namespace nana + +#include #endif diff --git a/include/nana/gui/detail/basic_window.hpp b/include/nana/gui/detail/basic_window.hpp index ed2e1db4..038acdbb 100644 --- a/include/nana/gui/detail/basic_window.hpp +++ b/include/nana/gui/detail/basic_window.hpp @@ -1,4 +1,4 @@ -/* +/** * A Basic Window Widget Definition * Nana C++ Library(http://www.nanapro.org) * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) @@ -7,14 +7,16 @@ * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) * - * @file: nana/gui/detail/basic_window.hpp + * @file nana/gui/detail/basic_window.hpp + * @brief A Basic Window Widget Definition */ #ifndef NANA_GUI_DETAIL_BASIC_WINDOW_HPP #define NANA_GUI_DETAIL_BASIC_WINDOW_HPP +#include #include "drawer.hpp" #include "events_holder.hpp" -#include "widget_colors.hpp" +#include "widget_geometrics.hpp" #include "widget_notifier_interface.hpp" #include #include @@ -30,47 +32,52 @@ namespace detail invisible, visible, displayed }; - class caret_descriptor + class caret + : public caret_interface { public: - typedef basic_window core_window_t; + caret(basic_window* owner, const size& size); + ~caret(); - caret_descriptor(core_window_t*, unsigned width, unsigned height); - ~caret_descriptor(); - void set_active(bool); - core_window_t* window() const; - void position(int x, int y); - void effective_range(::nana::rectangle); - ::nana::point position() const; - void visible(bool isshow); - bool visible() const; - ::nana::size size() const; - void size(const ::nana::size&); + void activate(bool activity); + basic_window* owner() const noexcept; void update(); - private: - core_window_t* wd_; - ::nana::point point_; - ::nana::size size_; - ::nana::size paint_size_; - visible_state visible_state_; - bool out_of_range_; - ::nana::rectangle effective_range_; - };//end class caret_descriptor + public: + //Implement caret_interface functions - //tab_type - //@brief: Define some constant about tab category, these flags can be combine with operator | + //This function is useless for class caret, see caret_proxy. + void disable_throw() noexcept override; + void effective_range(const rectangle& r) override; + void position(const point& pos) override; + nana::point position() const override; + size dimension() const override; + void dimension(const size& s); + void visible(bool visibility) override; + bool visible() const override; + private: + basic_window * owner_; + point position_; + size size_; + size visual_size_; + visible_state visibility_{ visible_state::invisible }; + bool out_of_range_{ false }; + rectangle effect_range_; + };//end class caret + + + /// Define some constant about tab category, these flags can be combine with operator | struct tab_type { enum t { - none, //process by nana - tabstop, //move to the next tabstop window - eating, //process by current window + none, ///< process by nana + tabstop, ///< move to the next tabstop window + eating, ///< process by current window }; }; - //struct basic_window - //@brief: a window data structure descriptor + + /// a window data structure descriptor struct basic_window : public events_holder { @@ -87,8 +94,7 @@ namespace detail bool rendered; }; - //basic_window - //@brief: constructor for the root window + /// constructor for the root window basic_window(basic_window* owner, std::unique_ptr&&, category::root_tag**); template @@ -105,23 +111,26 @@ namespace detail ~basic_window(); - //bind_native_window - //@brief: bind a native window and baisc_window + /// 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; bool belong_to_lazy() const; - const basic_window * child_caret() const; //Returns a child which owns a caret + const basic_window * child_caret() const; ///< Returns the child which owns the caret bool is_draw_through() const; ///< Determines whether it is a draw-through window. basic_window * seek_non_lite_widget_ancestor() const; + + void set_action(mouse_action); public: - //Override event_holder + /// Override event_holder bool set_events(const std::shared_ptr&) override; general_events * get_events() const override; private: @@ -131,7 +140,7 @@ namespace detail #if defined(NANA_LINUX) || defined(NANA_MACOS) point pos_native; #endif - point pos_root; //coordinate for root window + point pos_root; ///< coordinates of the root window point pos_owner; size dimension; ::nana::size min_track_size; @@ -146,9 +155,9 @@ namespace detail basic_window *owner; native_string_type title; - ::nana::detail::drawer drawer; //Self Drawer with owen graphics - basic_window* root_widget; //A pointer refers to the root basic window, if the window is a root, the pointer refers to itself. - paint::graphics* root_graph; //Refer to the root buffer graphics + ::nana::detail::drawer drawer; ///< Self Drawer with owen graphics + basic_window* root_widget; ///< A pointer refers to the root basic window, if the window is a root, the pointer refers to itself. + paint::graphics* root_graph; ///< Refer to the root buffer graphics cursor predef_cursor; std::unique_ptr widget_notifier; @@ -156,30 +165,33 @@ namespace detail { bool enabled :1; bool dbl_click :1; - bool captured :1; //if mouse button is down, it always receive mouse move even the mouse is out of its rectangle + bool captured :1; ///< if mouse button is down, it always receive mouse move even the mouse is out of its rectangle bool modal :1; - bool take_active:1; //If take_active is false, other.active_window still keeps the focus. + bool take_active:1; ///< If take_active is false, other.active_window still keeps the focus. bool refreshing :1; bool destroying :1; - bool dropable :1; //Whether the window has make mouse_drop event. - bool fullscreen :1; //When the window is maximizing whether it fit for fullscreen. + bool dropable :1; ///< Whether the window has make mouse_drop event. + bool fullscreen :1; ///< When the window is maximizing whether it fit for fullscreen. bool borderless :1; - bool make_bground_declared : 1; //explicitly make bground for bground effects - bool ignore_menubar_focus : 1; //A flag indicates whether the menubar sets the focus. - 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 make_bground_declared : 1; ///< explicitly make bground for bground effects + bool ignore_menubar_focus : 1; ///< A flag indicates whether the menubar sets the focus. + bool ignore_mouse_focus : 1; ///< A flag indicates whether the widget accepts focus when clicking on it + bool space_click_enabled : 1; ///< A flag indicates whether enable mouse_down/click/mouse_up when pressing and releasing whitespace key. unsigned Reserved :18; - unsigned char tab; //indicate a window that can receive the keyboard TAB + unsigned char tab; ///< indicate a window that can receive the keyboard TAB mouse_action action; + mouse_action action_before; }flags; - struct + + struct annex_components { - caret_descriptor* caret; + caret* caret_ptr{ nullptr }; + + //The following pointers refer to the widget's object. std::shared_ptr events_ptr; - }together; - - widget_colors* scheme{ nullptr }; + widget_geometrics* scheme{ nullptr }; + }annex; struct { @@ -190,15 +202,19 @@ 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 { - container frames; //initialization is null, it will be created while creating a frame widget. Refer to WindowManager::create_frame +#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}; @@ -210,32 +226,35 @@ namespace detail cursor state_cursor{nana::cursor::arrow}; basic_window* state_cursor_window{ nullptr }; - std::function draw_through; // A draw through renderer for root widgets. + std::function draw_through; ///< A draw through renderer for root widgets. }; const category::flags category; - basic_window *active_window; //if flags.take_active is false, the active_window still keeps the focus, - //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. + basic_window *active_window; ///< if flags.take_active is false, the active_window still keeps the focus, + ///< 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; union { attr_root_tag * root; +#ifndef WIDGET_FRAME_DEPRECATED attr_frame_tag * frame; +#endif }attribute; other_tag(category::flags); ~other_tag(); }other; - native_window_type root; //root Window handle - unsigned thread_id; //the identifier of the thread that created the window. + native_window_type root; ///< root Window handle + unsigned thread_id; ///< the identifier of the thread that created the window. unsigned index; container children; }; }//end namespace detail }//end namespace nana +#include #endif diff --git a/include/nana/gui/detail/bedrock.hpp b/include/nana/gui/detail/bedrock.hpp index e4872711..cda91c98 100644 --- a/include/nana/gui/detail/bedrock.hpp +++ b/include/nana/gui/detail/bedrock.hpp @@ -1,20 +1,21 @@ -/* +/** * A Bedrock Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) * - * @file: nana/gui/detail/bedrock.hpp + * @file nana/gui/detail/bedrock.hpp + * + * @brief A Bedrock Implementation */ #ifndef NANA_GUI_DETAIL_BEDROCK_HPP #define NANA_GUI_DETAIL_BEDROCK_HPP #include "general_events.hpp" #include "color_schemes.hpp" -#include "internal_scope_guard.hpp" namespace nana { @@ -26,12 +27,14 @@ namespace detail struct basic_window; class window_manager; - //class bedrock - //@brief: bedrock is a fundamental core component, it provides a abstract to the OS platform - // and some basic functions. + + /// @brief fundamental core component, it provides an abstraction to the OS platform and some basic functions. class bedrock { bedrock(); + + bedrock(const bedrock&) = delete; + bedrock& operator=(const bedrock&) = delete; public: using core_window_t = basic_window; @@ -41,14 +44,13 @@ namespace detail ~bedrock(); void pump_event(window, bool is_modal); - void map_thread_root_buffer(core_window_t*, bool forced, const rectangle* update_area = nullptr); + void flush_surface(core_window_t*, bool forced, const rectangle* update_area = nullptr); static int inc_window(unsigned tid = 0); thread_context* open_thread_context(unsigned tid = 0); thread_context* get_thread_context(unsigned tid = 0); void remove_thread_context(unsigned tid = 0); static bedrock& instance(); - ::nana::category::flags category(core_window_t*); core_window_t* focus(); void set_menubar_taken(core_window_t*); @@ -62,8 +64,9 @@ namespace detail void erase_menu(bool try_destroy); void get_key_state(arg_keyboard&); - bool set_keyboard_shortkey(bool yes); - bool whether_keyboard_shortkey() const; + + bool shortkey_occurred(bool status); + bool shortkey_occurred() const; element_store& get_element_store() const; void map_through_widgets(core_window_t*, native_drawable_type); @@ -71,6 +74,7 @@ namespace detail void event_expose(core_window_t *, bool exposed); void event_move(core_window_t*, int x, int y); bool event_msleave(core_window_t*); + void event_focus_changed(core_window_t* root_wd, native_window_type receiver, bool getting); void thread_context_destroy(core_window_t*); void thread_context_lazy_refresh(); void update_cursor(core_window_t*); @@ -78,16 +82,13 @@ namespace detail void define_state_cursor(core_window_t*, nana::cursor, thread_context*); void undefine_state_cursor(core_window_t*, thread_context*); - widget_colors& get_scheme_template(scheme_factory_base&&); - widget_colors* make_scheme(scheme_factory_base&&); - + color_schemes& scheme(); events_operation& evt_operation(); window_manager& wd_manager(); void manage_form_loader(core_window_t*, bool insert_or_remove); public: bool emit(event_code, core_window_t*, const event_arg&, bool ask_update, thread_context*); - bool emit_drawer(event_code, core_window_t*, const event_arg&, thread_context*); private: void _m_emit_core(event_code, core_window_t*, bool draw_only, const event_arg&); void _m_event_filter(event_code, core_window_t*, thread_context*); diff --git a/include/nana/gui/detail/bedrock_pi_data.hpp b/include/nana/gui/detail/bedrock_pi_data.hpp index 3154a6b3..7404777b 100644 --- a/include/nana/gui/detail/bedrock_pi_data.hpp +++ b/include/nana/gui/detail/bedrock_pi_data.hpp @@ -1,6 +1,8 @@ #ifndef NANA_DETAIL_BEDROCK_PI_DATA_HPP #define NANA_DETAIL_BEDROCK_PI_DATA_HPP +#include + #include #include "color_schemes.hpp" #include "events_operation.hpp" @@ -17,7 +19,20 @@ namespace nana 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/color_schemes.hpp b/include/nana/gui/detail/color_schemes.hpp index 9482f084..79e09775 100644 --- a/include/nana/gui/detail/color_schemes.hpp +++ b/include/nana/gui/detail/color_schemes.hpp @@ -1,7 +1,7 @@ /* * Color Schemes * Nana C++ Library(http://www.nanapro.org) -* Copyright(C) 2003-2014 Jinhao(cnjinhao@hotmail.com) +* Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -13,26 +13,27 @@ #ifndef NANA_DETAIL_COLOR_SCHEMES_HPP #define NANA_DETAIL_COLOR_SCHEMES_HPP -#include "widget_colors.hpp" +#include "widget_geometrics.hpp" namespace nana { namespace detail { - class scheme_factory_base + class scheme_factory_interface { public: struct factory_identifier{}; - virtual ~scheme_factory_base() = default; + virtual ~scheme_factory_interface() = default; virtual factory_identifier* get_id() const = 0; - virtual widget_colors* create() = 0; - virtual widget_colors* create(widget_colors&) = 0; + virtual widget_geometrics* create() = 0; + virtual widget_geometrics* create(widget_geometrics&) = 0; }; + template class scheme_factory - : public scheme_factory_base + : public scheme_factory_interface { private: factory_identifier* get_id() const override @@ -40,12 +41,12 @@ namespace nana return &fid_; } - widget_colors* create() override + widget_geometrics* create() override { return (new Scheme); } - widget_colors* create(widget_colors& other) override + widget_geometrics* create(widget_geometrics& other) override { return (new Scheme(static_cast(other))); } @@ -54,7 +55,7 @@ namespace nana }; template - scheme_factory_base::factory_identifier scheme_factory::fid_; + scheme_factory_interface::factory_identifier scheme_factory::fid_; class color_schemes { @@ -64,13 +65,13 @@ namespace nana color_schemes& operator=(const color_schemes&) = delete; color_schemes& operator=(color_schemes&&) = delete; public: - using scheme = widget_colors; + using scheme = widget_geometrics; color_schemes(); ~color_schemes(); - scheme& scheme_template(scheme_factory_base&&); - scheme* create(scheme_factory_base&&); + scheme& scheme_template(scheme_factory_interface&&); + scheme* create(scheme_factory_interface&&); private: implement * impl_; }; diff --git a/include/nana/gui/detail/drawer.hpp b/include/nana/gui/detail/drawer.hpp index b680ec6a..472f822e 100644 --- a/include/nana/gui/detail/drawer.hpp +++ b/include/nana/gui/detail/drawer.hpp @@ -13,7 +13,7 @@ #ifndef NANA_GUI_DETAIL_DRAWER_HPP #define NANA_GUI_DETAIL_DRAWER_HPP -#include +#include #include "general_events.hpp" #include #include @@ -28,14 +28,23 @@ namespace nana } class drawer_trigger - : ::nana::noncopyable, ::nana::nonmovable { friend class detail::drawer; + + //Noncopyable + drawer_trigger(const drawer_trigger&) = delete; + drawer_trigger& operator=(const drawer_trigger&) = delete; + + //Nonmovable + drawer_trigger(drawer_trigger&&) = delete; + drawer_trigger& operator=(drawer_trigger&&) = delete; + public: using widget_reference = widget&; using graph_reference = paint::graphics&; - virtual ~drawer_trigger(); + drawer_trigger() = default; + virtual ~drawer_trigger() = default; virtual void attached(widget_reference, graph_reference); //none-const virtual void detached(); //none-const @@ -72,21 +81,11 @@ namespace nana { struct basic_window; - namespace dynamic_drawing - { - //declaration - class object; - } - //@brief: Every window has a drawer, the drawer holds a drawer_trigger for // a widget. class drawer : nana::noncopyable, nana::nonmovable { - enum{ - event_size = static_cast(event_code::end) - }; - enum class method_state { pending, @@ -94,6 +93,7 @@ namespace nana not_overrided }; public: + drawer(); ~drawer(); void bind(basic_window*); @@ -126,48 +126,48 @@ namespace nana void* draw(std::function &&, bool diehard); void erase(void* diehard); private: - void _m_bground_pre(); - void _m_bground_end(); - void _m_draw_dynamic_drawing_object(); + void _m_effect_bground(bool before); bool _m_lazy_decleared() const; + method_state& _m_mth_state(int pos); template void _m_emit(event_code evt_code, const Arg& arg, Mfptr mfptr) { const int pos = static_cast(evt_code); - if (realizer_ && (method_state::not_overrided != mth_state_[pos])) - { - _m_bground_pre(); - if (method_state::pending == mth_state_[pos]) + auto realizer = this->realizer(); + auto & mth_state = _m_mth_state(pos); + + if (realizer && (method_state::not_overrided != mth_state)) + { + _m_effect_bground(true); + + if (method_state::pending == mth_state) { - (realizer_->*mfptr)(graphics, arg); + (realizer->*mfptr)(graphics, arg); //Check realizer, when the window is closed in that event handler, the drawer will be //detached and realizer will be a nullptr - if(realizer_) - mth_state_[pos] = (realizer_->_m_overrided(evt_code) ? method_state::overrided : method_state::not_overrided); + if (realizer) + mth_state = (realizer->_m_overrided(evt_code) ? method_state::overrided : method_state::not_overrided); } else - (realizer_->*mfptr)(graphics, arg); + (realizer->*mfptr)(graphics, arg); if (_m_lazy_decleared()) - { - _m_draw_dynamic_drawing_object(); - _m_bground_end(); - } + _m_effect_bground(false); } } public: nana::paint::graphics graphics; private: - basic_window* core_window_{nullptr}; - drawer_trigger* realizer_{nullptr}; - std::vector dynamic_drawing_objects_; - bool refreshing_{false}; - method_state mth_state_[event_size]; + struct data_implement; + + data_implement * const data_impl_; }; }//end namespace detail }//end namespace nana +#include + #endif diff --git a/include/nana/gui/detail/effects_renderer.hpp b/include/nana/gui/detail/effects_renderer.hpp index 0304fcb9..155fcb78 100644 --- a/include/nana/gui/detail/effects_renderer.hpp +++ b/include/nana/gui/detail/effects_renderer.hpp @@ -24,7 +24,7 @@ namespace nana{ return object; } - unsigned weight() const + constexpr unsigned weight() const { return 2; } @@ -122,22 +122,37 @@ namespace nana{ } } + rectangle wd_r{ wd->pos_root, wd->dimension }; + wd_r.pare_off(-static_cast(this->weight())); //Render for (auto & rd : rd_set) - _m_render_edge_nimbus(rd.second, rd.first); + { + auto other_wd = rd.second; + + if (other_wd != wd) + { + rectangle other_r{ other_wd->pos_root, other_wd->dimension }; + other_r.pare_off(-static_cast(this->weight())); + if (!overlapped(wd_r, other_r)) + continue; + } + _m_render_edge_nimbus(other_wd, rd.first); + } } private: static bool _m_edge_nimbus(core_window_t * focused_wd, core_window_t * wd) { if((focused_wd == wd) && (static_cast(wd->effect.edge_nimbus) & static_cast(effects::edge_nimbus::active))) return true; - else if((static_cast(wd->effect.edge_nimbus) & static_cast(effects::edge_nimbus::over)) && (wd->flags.action == mouse_action::over)) + else if((static_cast(wd->effect.edge_nimbus) & static_cast(effects::edge_nimbus::over)) && (wd->flags.action == mouse_action::hovered)) return true; return false; } void _m_render_edge_nimbus(core_window_t* wd, const nana::rectangle & visual) { + wd->flags.action_before = wd->flags.action; + auto r = visual; r.pare_off(-static_cast(weight())); rectangle good_r; @@ -159,12 +174,12 @@ namespace nana{ good_r.x = good_r.y = 1; good_r.width = r.width - 2; good_r.height = r.height - 2; - pixbuf.rectangle(good_r, wd->scheme->activated.get_color(), 0.95, false); + pixbuf.rectangle(good_r, wd->annex.scheme->activated.get_color(), 0.95, false); good_r.x = good_r.y = 0; good_r.width = r.width; good_r.height = r.height; - pixbuf.rectangle(good_r, wd->scheme->activated.get_color(), 0.4, false); + pixbuf.rectangle(good_r, wd->annex.scheme->activated.get_color(), 0.4, false); pixbuf.pixel(0, 0, px0); pixbuf.pixel(r.width - 1, 0, px1); diff --git a/include/nana/gui/detail/element_store.hpp b/include/nana/gui/detail/element_store.hpp index 0035d4be..3ac5b242 100644 --- a/include/nana/gui/detail/element_store.hpp +++ b/include/nana/gui/detail/element_store.hpp @@ -15,9 +15,13 @@ #include #include + #include #include +#include + + namespace nana { namespace detail @@ -48,5 +52,6 @@ namespace detail }; }//end namespace detail } +#include #endif diff --git a/include/nana/gui/detail/events_operation.hpp b/include/nana/gui/detail/events_operation.hpp index 78f5c432..e28b9af3 100644 --- a/include/nana/gui/detail/events_operation.hpp +++ b/include/nana/gui/detail/events_operation.hpp @@ -3,7 +3,6 @@ #include #include -#include #if defined(STD_THREAD_NOT_SUPPORTED) #include @@ -18,15 +17,12 @@ namespace nana class events_operation { public: - void make(window, const std::shared_ptr &); - void umake(window); void register_evt(event_handle); void cancel(event_handle); void erase(event_handle); private: std::recursive_mutex mutex_; std::unordered_set handles_; - std::unordered_map> evt_table_; }; }//end namespace detail }//end namespace nana diff --git a/include/nana/gui/detail/general_events.hpp b/include/nana/gui/detail/general_events.hpp index 8d75dc9e..5992f1a0 100644 --- a/include/nana/gui/detail/general_events.hpp +++ b/include/nana/gui/detail/general_events.hpp @@ -12,18 +12,22 @@ #ifndef NANA_DETAIL_GENERAL_EVENTS_HPP #define NANA_DETAIL_GENERAL_EVENTS_HPP +#include + #include #include "event_code.hpp" #include "internal_scope_guard.hpp" #include #include -#include #include namespace nana { namespace detail { + bool check_window(window); + void events_operation_register(event_handle); + class event_interface { public: @@ -38,11 +42,51 @@ namespace nana virtual event_interface* get_event() const = 0; }; - void events_operation_register(event_handle); - void events_operation_cancel(event_handle); + + struct docker_base + : public docker_interface + { + event_interface * event_ptr; + bool flag_deleted{ false }; + const bool unignorable; + + docker_base(event_interface*, bool unignorable_flag); + + detail::event_interface * get_event() const override; + }; + + class event_base + : public detail::event_interface + { + public: + ~event_base(); + + std::size_t length() const; + void clear() noexcept; + + void remove(event_handle evt) override; + protected: + //class emit_counter is a RAII helper for emitting count + //It is used for avoiding a try{}catch block which is required for some finial works when + //event handlers throw exceptions. Precondition event_base.dockers_ != nullptr. + class emit_counter + { + public: + emit_counter(event_base*); + ~emit_counter(); + private: + event_base * const evt_; + }; + + event_handle _m_emplace(detail::docker_interface*, bool in_front); + protected: + unsigned emitting_count_{ 0 }; + bool deleted_flags_{ false }; + std::vector * dockers_{ nullptr }; + }; }//end namespace detail - /// base clase for all event argument types + /// base class for all event argument types class event_arg { public: @@ -57,87 +101,44 @@ namespace nana struct general_events; - /// the type of the members of general_events + /** @brief the type of the members of general_events. + * + * It connect the functions to be called as response to the event and manages that chain of responses + * It is a functor, that get called to connect a "normal" response function, with normal "priority". + * If a response function need another priority (unignorable or called first) it will need to be connected with + * the specific connect function not with the operator() + * It also permit to "emit" that event, calling all the active responders. + */ template - class basic_event : public detail::event_interface + class basic_event : public detail::event_base { public: using arg_reference = const typename std::remove_reference::type &; private: struct docker - : public detail::docker_interface - { - basic_event * const event_ptr; + : public detail::docker_base + { + /// the callback/response function taking the typed argument std::function invoke; - bool flag_deleted{ false }; - bool unignorable{false}; - - docker(basic_event * s, std::function && ivk, bool unignorable_flag) - : event_ptr(s), invoke(std::move(ivk)), unignorable(unignorable_flag) + docker(basic_event * evt, std::function && ivk, bool unignorable_flag) + : docker_base(evt, unignorable_flag), invoke(std::move(ivk)) {} - docker(basic_event * s, const std::function & ivk, bool unignorable_flag) - : event_ptr(s), invoke(ivk), unignorable(unignorable_flag) + docker(basic_event * evt, const std::function & ivk, bool unignorable_flag) + : docker_base(evt, unignorable_flag), invoke(ivk) {} - - ~docker() - { - detail::events_operation_cancel(reinterpret_cast(this)); - } - - detail::event_interface * get_event() const override - { - return event_ptr; - } - }; - - //class emit_counter is a RAII helper for emitting count - //It is used for avoiding a try{}catch block which is required for some finial works when - //event handlers throw exceptions. - class emit_counter - { - public: - emit_counter(basic_event* evt) - : evt_{evt} - { - ++evt->emitting_count_; - } - - ~emit_counter() - { - if ((0 == --evt_->emitting_count_) && evt_->deleted_flags_) - { - evt_->deleted_flags_ = false; - for (auto i = evt_->dockers_->begin(); i != evt_->dockers_->end();) - { - if (static_cast(i->get())->flag_deleted) - i = evt_->dockers_->erase(i); - else - ++i; - } - } - } - private: - basic_event * const evt_; }; public: - /// It will get called firstly, because it is set at the beginning of the chain. + /// Creates an event handler at the beginning of event chain template event_handle connect_front(Function && fn) - { - internal_scope_guard lock; - if (nullptr == dockers_) - dockers_.reset(new std::vector>); - + { using prototype = typename std::remove_reference::type; - std::unique_ptr dck(new docker(this, factory::value>::build(std::forward(fn)), false)); - auto evt = reinterpret_cast(dck.get()); - dockers_->emplace(dockers_->begin(), std::move(dck)); - detail::events_operation_register(evt); - return evt; + return _m_emplace(new docker(this, factory::value>::build(std::forward(fn)), false), true); } + /// It will not get called if stop_propagation() was called. event_handle connect(void (*fn)(arg_reference)) { return connect([fn](arg_reference arg){ @@ -145,20 +146,12 @@ namespace nana }); } - /// It will not get called if stop_propagation() was called. + /// It will not get called if stop_propagation() was called, because it is set at the end of the chain.. template event_handle connect(Function && fn) { - internal_scope_guard lock; - if (nullptr == dockers_) - dockers_.reset(new std::vector>); - using prototype = typename std::remove_reference::type; - std::unique_ptr dck(new docker(this, factory::value>::build(std::forward(fn)), false)); - auto evt = reinterpret_cast(dck.get()); - dockers_->emplace_back(std::move(dck)); - detail::events_operation_register(evt); - return evt; + return _m_emplace(new docker(this, factory::value>::build(std::forward(fn)), false), false); } /// It will not get called if stop_propagation() was called. @@ -171,29 +164,13 @@ namespace nana /// It will get called because it is unignorable. template event_handle connect_unignorable(Function && fn, bool in_front = false) - { - internal_scope_guard lock; - if (nullptr == dockers_) - dockers_.reset(new std::vector>); - + { using prototype = typename std::remove_reference::type; - std::unique_ptr dck(new docker(this, factory::value>::build(std::forward(fn)), true)); - auto evt = reinterpret_cast(dck.get()); - if (in_front) - dockers_->emplace(dockers_->begin(), std::move(dck)); - else - dockers_->emplace_back(std::move(dck)); - detail::events_operation_register(evt); - return evt; + + return _m_emplace(new docker(this, factory::value>::build(std::forward(fn)), true), in_front); } - std::size_t length() const - { - internal_scope_guard lock; - return (nullptr == dockers_ ? 0 : dockers_->size()); - } - - void emit(arg_reference& arg) + void emit(arg_reference& arg, window window_handle) { internal_scope_guard lock; if (nullptr == dockers_) @@ -201,62 +178,37 @@ namespace nana emit_counter ec(this); - auto& dockers = *dockers_; - const auto dockers_len = dockers.size(); - //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. - for (std::size_t pos = 0; pos < dockers_len; ++pos) + + auto i = dockers_->data(); + auto const end = i + dockers_->size(); + + for (; i != end; ++i) { - auto docker_ptr = static_cast(dockers[pos].get()); - if (docker_ptr->flag_deleted) + if (static_cast(*i)->flag_deleted) continue; - docker_ptr->invoke(arg); + static_cast(*i)->invoke(arg); + + if (window_handle && (!detail::check_window(window_handle))) + break; + if (arg.propagation_stopped()) { - for (++pos; pos < dockers_len; ++pos) + for (++i; i != end; ++i) { - auto docker_ptr = static_cast(dockers[pos].get()); - if (!docker_ptr->unignorable || docker_ptr->flag_deleted) + if (!static_cast(*i)->unignorable || static_cast(*i)->flag_deleted) continue; - docker_ptr->invoke(arg); + static_cast(*i)->invoke(arg); + if (window_handle && (!detail::check_window(window_handle))) + break; } break; } } } - - void clear() - { - internal_scope_guard lock; - if (dockers_) - dockers_.reset(); - } - - void remove(event_handle evt) override - { - internal_scope_guard lock; - if (dockers_) - { - for (auto i = dockers_->begin(), end = dockers_->end(); i != end; ++i) - { - if (reinterpret_cast(evt) == i->get()) - { - //Checks whether this event is working now. - if (emitting_count_ > 1) - { - static_cast(i->get())->flag_deleted = true; - deleted_flags_ = true; - } - else - dockers_->erase(i); - break; - } - } - } - } private: template struct factory @@ -294,11 +246,6 @@ namespace nana }; } - static std::function build_second(fn_type&& fn, void(fn_type::*)(arg_reference)) - { - return std::move(fn); - } - template static std::function build_second(Tfn&& fn, Ret(fn_type::*)()const) { @@ -308,11 +255,36 @@ namespace nana }; } + static std::function build_second(fn_type&& fn, void(fn_type::*)(arg_reference)) + { + return std::move(fn); + } + static std::function build_second(fn_type&& fn, void(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; + } + template static std::function build_second(Tfn&& fn, Ret(fn_type::*)(Arg2)) { @@ -322,7 +294,7 @@ namespace nana fn(arg); }; } - + template static std::function build_second(Tfn&& fn, Ret(fn_type::*)(Arg2)const) { @@ -333,7 +305,7 @@ namespace nana }; } }; - + template struct factory < std::function, false> { @@ -413,26 +385,22 @@ namespace nana }; } }; - private: - unsigned emitting_count_{ 0 }; - bool deleted_flags_{ false }; - std::unique_ptr>> dockers_; }; - + struct arg_mouse : public event_arg { - event_code evt_code; ///< + event_code evt_code; ///< what kind of mouse event? ::nana::window window_handle; ///< A handle to the event window ::nana::point pos; ///< cursor position in the event window ::nana::mouse button; ///< indicates a button which triggers the event - bool left_button; ///< mouse left button is pressed? - bool mid_button; ///< mouse middle button is pressed? - bool right_button; ///< mouse right button is pressed? - bool alt; ///< keyboard alt is pressed? - bool shift; ///< keyboard Shift is pressed? - bool ctrl; ///< keyboard Ctrl is pressed? + bool left_button; ///< true if mouse left button is pressed + bool mid_button; ///< true if mouse middle button is pressed + bool right_button; ///< true if mouse right button is pressed + bool alt; ///< true if keyboard alt is pressed + bool shift; ///< true if keyboard Shift is pressed + bool ctrl; ///< true if keyboard Ctrl is pressed /// Checks if left button is operated, bool is_left_button() const @@ -441,7 +409,8 @@ namespace nana } }; - /// in arg_wheel event_code is event_code::mouse_wheel + /// \brief in arg_wheel event_code is event_code::mouse_wheel + /// The type arg_wheel is derived from arg_mouse, a handler /// with prototype void(const arg_mouse&) can be set for mouse_wheel. struct arg_wheel : public arg_mouse @@ -471,9 +440,18 @@ namespace nana struct arg_focus : public event_arg { - ::nana::window window_handle; ///< A handle to the event window - ::nana::native_window_type receiver; ///< it is a native window handle, and specified which window receives focus - bool getting; ///< the window received focus? + /// A constant to indicate how keyboard focus emitted. + enum class reason + { + general, ///< the focus is received by OS native window manager. + tabstop, ///< the focus is received by pressing tab. + mouse_press ///< the focus is received by pressing a mouse button. + }; + + ::nana::window window_handle; ///< A handle to the event window + ::nana::native_window_type receiver; ///< it is a native window handle, and specified which window receives focus + bool getting; ///< the window received focus? + reason focus_reason; ///< determines how the widget receives keyboard focus, it is ignored when 'getting' is equal to false }; struct arg_keyboard : public event_arg @@ -481,7 +459,7 @@ namespace nana event_code evt_code; ///< it is event_code::key_press in current event ::nana::window window_handle; ///< A handle to the event window mutable wchar_t key; ///< the key corresponding to the key pressed - mutable bool ignore; ///< this member is not used + mutable bool ignore; ///< this member is only available for key_char event, set 'true' to ignore the input. bool ctrl; ///< keyboard Ctrl is pressed? bool shift; ///< keyboard Shift is pressed }; @@ -518,11 +496,11 @@ namespace nana { ::nana::window window_handle; ///< A handle to the event window }; - + /// a higher level event argument than just mouse down struct arg_click : public event_arg { ::nana::window window_handle; ///< A handle to the event window - const arg_mouse* mouse_args; ///< If it is not null, it refers to the mouse arguments for click event emitted by mouse, nullptr otherwise. + const arg_mouse* mouse_args{}; ///< If it is not null, it refers to the mouse arguments for click event emitted by mouse, nullptr otherwise. }; /// provides some fundamental events that every widget owns. @@ -562,4 +540,6 @@ namespace nana }//end namespace detail }//end namespace nana +#include + #endif diff --git a/include/nana/gui/detail/handle_manager.hpp b/include/nana/gui/detail/handle_manager.hpp index 7e8f670d..6319e319 100644 --- a/include/nana/gui/detail/handle_manager.hpp +++ b/include/nana/gui/detail/handle_manager.hpp @@ -25,6 +25,8 @@ #include #include +#include + namespace nana { @@ -171,7 +173,7 @@ namespace nana { is_queue::value, std::vector >::erase(handle, queue_); cacher_.insert(handle, false); - trash_.emplace_back(i->first, i->second); + trash_.push_back(*i); holder_.erase(i); } } @@ -288,4 +290,6 @@ namespace nana };//end class handle_manager }//end namespace detail }// end namespace nana +#include + #endif diff --git a/include/nana/gui/detail/inner_fwd_implement.hpp b/include/nana/gui/detail/inner_fwd_implement.hpp index 90589d3c..a1bc1aaf 100644 --- a/include/nana/gui/detail/inner_fwd_implement.hpp +++ b/include/nana/gui/detail/inner_fwd_implement.hpp @@ -14,6 +14,7 @@ #ifndef NANA_GUI_INNER_FWD_IMPLEMENT_HPP #define NANA_GUI_INNER_FWD_IMPLEMENT_HPP +#include #include "inner_fwd.hpp" #include "basic_window.hpp" #include "../../paint/graphics.hpp" @@ -111,20 +112,19 @@ namespace nana{ struct root_misc { - typedef basic_window core_window_t; - - core_window_t * window; + basic_window * window; nana::paint::graphics root_graph; shortkey_container shortkeys; - struct condition_tag + struct condition_rep { - core_window_t* pressed{nullptr}; //The handle to a window which has been pressed by pressing left button of mouse. - core_window_t* pressed_by_space{ nullptr }; //The handle to a window which has been pressed by pressing spacebar. - core_window_t* hovered{nullptr}; //the latest window that mouse moved + bool ignore_tab{ false }; //ignore tab when the focus is changed by TAB key. + basic_window* pressed{ nullptr }; //The handle to a window which has been pressed by mouse left button. + basic_window* pressed_by_space{ nullptr }; //The handle to a window which has been pressed by SPACEBAR key. + basic_window* hovered{ nullptr }; //the latest window that mouse moved }condition; - root_misc(core_window_t * wd, unsigned width, unsigned height) + root_misc(basic_window * wd, unsigned width, unsigned height) : window(wd), root_graph({ width, height }) {} @@ -172,4 +172,7 @@ namespace nana{ }; } }//end namespace nana + +#include + #endif //NANA_GUI_INNER_FWD_IMPLEMENT_HPP diff --git a/include/nana/gui/detail/internal_scope_guard.hpp b/include/nana/gui/detail/internal_scope_guard.hpp index c4bdaaf6..6ad8ae80 100644 --- a/include/nana/gui/detail/internal_scope_guard.hpp +++ b/include/nana/gui/detail/internal_scope_guard.hpp @@ -1,7 +1,7 @@ /* * Forward Declaration of Internal Scope Guard * Nana C++ Library(http://www.nanapro.org) -* Copyright(C) 2003-2014 Jinhao(cnjinhao@hotmail.com) +* Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -26,6 +26,18 @@ namespace nana internal_scope_guard(); ~internal_scope_guard(); }; + + class internal_revert_guard + { + internal_revert_guard(const internal_revert_guard&) = delete; + internal_revert_guard(internal_revert_guard&&) = delete; + + internal_revert_guard& operator=(const internal_revert_guard&) = delete; + internal_revert_guard& operator=(internal_revert_guard&&) = delete; + public: + internal_revert_guard(); + ~internal_revert_guard(); + }; } #endif \ No newline at end of file diff --git a/include/nana/gui/detail/native_window_interface.hpp b/include/nana/gui/detail/native_window_interface.hpp index aade67a5..737423bf 100644 --- a/include/nana/gui/detail/native_window_interface.hpp +++ b/include/nana/gui/detail/native_window_interface.hpp @@ -1,7 +1,7 @@ /* * Platform Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -36,6 +36,9 @@ namespace detail using native_string_type = ::nana::detail::native_string_type; + //Execute a function in a thread which is associated with the specified native window. + static void affinity_execute(native_window_type, const std::function&); + static nana::size primary_monitor_size(); static rectangle screen_area_from_point(const point&); static window_result create_window(native_window_type, bool nested, const rectangle&, const appearance&); diff --git a/include/nana/gui/detail/widget_colors.hpp b/include/nana/gui/detail/widget_geometrics.hpp similarity index 76% rename from include/nana/gui/detail/widget_colors.hpp rename to include/nana/gui/detail/widget_geometrics.hpp index d4d81e1a..1cb8cd3e 100644 --- a/include/nana/gui/detail/widget_colors.hpp +++ b/include/nana/gui/detail/widget_geometrics.hpp @@ -1,17 +1,17 @@ /* -* Color Schemes +* Widget Geometrics * Nana C++ Library(http://www.nanapro.org) -* Copyright(C) 2003-2014 Jinhao(cnjinhao@hotmail.com) +* Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) * -* @file: nana/gui/widget_colors.hpp +* @file: nana/gui/widget_geometrics.hpp * @description: */ -#ifndef NANA_DETAIL_WIDGET_COLORS_HPP -#define NANA_DETAIL_WIDGET_COLORS_HPP +#ifndef NANA_DETAIL_WIDGET_GEOMETRICS_HPP +#define NANA_DETAIL_WIDGET_GEOMETRICS_HPP #include #include @@ -33,9 +33,9 @@ namespace nana std::shared_ptr color_; };//end namespace color_proxy - struct widget_colors + struct widget_geometrics { - virtual ~widget_colors() = default; + virtual ~widget_geometrics() = default; color_proxy activated{ static_cast(0x60C8FD) }; color_proxy background{colors::button_face}; diff --git a/include/nana/gui/detail/window_layout.hpp b/include/nana/gui/detail/window_layout.hpp index f4bdfe0b..34987291 100644 --- a/include/nana/gui/detail/window_layout.hpp +++ b/include/nana/gui/detail/window_layout.hpp @@ -12,6 +12,7 @@ #ifndef NANA_GUI_DETAIL_WINDOW_LAYOUT_HPP #define NANA_GUI_DETAIL_WINDOW_LAYOUT_HPP +#include #include #include @@ -84,5 +85,7 @@ namespace detail }//end namespace detail }//end namespace nana +#include + #endif //NANA_GUI_DETAIL_WINDOW_LAYOUT_HPP diff --git a/include/nana/gui/detail/window_manager.hpp b/include/nana/gui/detail/window_manager.hpp index b04c32dc..268547c0 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-2014 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -10,14 +10,16 @@ * @file: nana/gui/detail/window_manager.hpp * * - * destroy method destroys a window handle and the handles of its children, but it doesn't delete the handle which type is a root window or a frame - * destroy_handle method just destroys the handle which type is a root window or a frame + * destroy method destroys a window handle and the handles of its children, but it doesn't delete the handle which type is a root window + * destroy_handle method just destroys the handle which type is a root window * */ #ifndef NANA_GUI_DETAIL_WINDOW_MANAGER_HPP #define NANA_GUI_DETAIL_WINDOW_MANAGER_HPP +#include + #include #include "window_layout.hpp" #include "event_code.hpp" @@ -92,10 +94,12 @@ 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 @@ -104,13 +108,13 @@ namespace detail //destroy_handle //@brief: Delete window handle, the handle type must be a root and a frame. + + // Deletes a window whose category type is a root type or a frame type. void destroy_handle(core_window_t*); void default_icon(const paint::image& _small_icon, const paint::image& big_icon); void icon(core_window_t*, const paint::image& small_icon, const paint::image& big_icon); - //show - //@brief: show or hide a window bool show(core_window_t* wd, bool visible); core_window_t* find_window(native_window_type root, int x, int y); @@ -129,20 +133,20 @@ namespace detail bool update(core_window_t*, bool redraw, bool force, const rectangle* update_area = nullptr); void refresh_tree(core_window_t*); - bool do_lazy_refresh(core_window_t*, bool force_copy_to_screen); + bool do_lazy_refresh(core_window_t*, bool force_copy_to_screen, bool refresh_tree = false); 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); + core_window_t* set_focus(core_window_t*, bool root_has_been_focused, arg_focus::reason); core_window_t* capture_redirect(core_window_t*); - void capture_ignore_children(bool ignore); + bool capture_window_entered(int root_x, int root_y, bool& prev); core_window_t * capture_window() const; - core_window_t* capture_window(core_window_t*, bool value); + void capture_window(core_window_t*, bool capture, bool ignore_children_if_captured); void enable_tabstop(core_window_t*); core_window_t* tabstop(core_window_t*, bool forward) const; //forward means move to next in logic. @@ -195,4 +199,7 @@ namespace detail };//end class window_manager }//end namespace detail }//end namespace nana + +#include + #endif diff --git a/include/nana/gui/dragger.hpp b/include/nana/gui/dragger.hpp index e34094ef..9e6bbbf2 100644 --- a/include/nana/gui/dragger.hpp +++ b/include/nana/gui/dragger.hpp @@ -12,6 +12,7 @@ #ifndef NANA_GUI_DRAGGER_HPP #define NANA_GUI_DRAGGER_HPP +#include #include "basis.hpp" #include "../basic_types.hpp" #include "../traits.hpp" @@ -44,4 +45,5 @@ namespace nana dragger_impl_t * impl_; }; }//end namespace nana +#include #endif diff --git a/include/nana/gui/drawing.hpp b/include/nana/gui/drawing.hpp index fb1b966b..06ecece6 100644 --- a/include/nana/gui/drawing.hpp +++ b/include/nana/gui/drawing.hpp @@ -11,6 +11,8 @@ */ #ifndef NANA_GUI_DRAWING_HPP #define NANA_GUI_DRAWING_HPP + +#include #include "widgets/widget.hpp" #include "../traits.hpp" namespace nana @@ -46,4 +48,6 @@ namespace nana window handle_; };//end class drawing }//end namespace nana + +#include #endif diff --git a/include/nana/gui/element.hpp b/include/nana/gui/element.hpp index 599aa87a..0ecfdad1 100644 --- a/include/nana/gui/element.hpp +++ b/include/nana/gui/element.hpp @@ -1,7 +1,7 @@ /* * Elements of GUI Gadgets * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2014 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -12,6 +12,7 @@ #ifndef NANA_GUI_ELEMENT_HPP #define NANA_GUI_ELEMENT_HPP +#include #include #include #include @@ -88,7 +89,6 @@ namespace nana struct factory_interface : public detail::factory_abstract { - virtual ~factory_interface(){} virtual ElementInterface* create() const = 0; }; @@ -349,4 +349,5 @@ namespace nana }//end namespace element }//end namespace nana +#include #endif //NANA_GUI_ELEMENT_HPP diff --git a/include/nana/gui/filebox.hpp b/include/nana/gui/filebox.hpp index 3a9b9f4c..561dc5b7 100644 --- a/include/nana/gui/filebox.hpp +++ b/include/nana/gui/filebox.hpp @@ -1,4 +1,4 @@ -/* +/** * Filebox * Nana C++ Library(http://www.nanapro.org) * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) @@ -7,7 +7,9 @@ * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) * - * @file: nana/gui/filebox.hpp + * @file nana/gui/filebox.hpp + * @author Jinhao + * @brief a dialog to chose file(s), implemented "native" in windows but using nana for X11 */ #ifndef NANA_GUI_FILEBOX_HPP @@ -37,18 +39,20 @@ namespace nana /// Change owner window void owner(window); - /// specify a title for the dialog + /// Set a new title for the dialog /// @param string a text for title - /// @return old title. - ::std::string title( ::std::string new_title); ///< . Set a new title for the dialog and \return the old title + /// @return the old title. + ::std::string title( ::std::string new_title); - /** @brief specify a suggestion directory - * @param string a path of initial directory + /** @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. */ - filebox& init_path(const ::std::string&); ///< Suggested init path used to locate a directory when the filebox starts. + filebox& init_path(const ::std::string& initial_directory); + filebox& init_file(const ::std::string&); ///< Init file, if it contains a path, the init path is replaced by the path of init file. - /// \brief Add a filetype filter. + + /// \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") @@ -68,8 +72,7 @@ namespace nana /// Display the filebox dialog bool show() const; - /// Display the filebox dialog - /// A function object method alternative to show() + /// a function object method alternative to show() to display the filebox dialog, bool operator()() const { return show(); diff --git a/include/nana/gui/layout_utility.hpp b/include/nana/gui/layout_utility.hpp index 51d4d4ea..56127aac 100644 --- a/include/nana/gui/layout_utility.hpp +++ b/include/nana/gui/layout_utility.hpp @@ -18,7 +18,7 @@ namespace nana { //overlap test if overlaped between r1 and r2 - bool overlap(const rectangle& r1, const rectangle& r2); + bool overlapped(const rectangle& r1, const rectangle& r2); // overlap, compute the overlap area between r1 and r2. the r is for root bool overlap(const rectangle& r1, const rectangle& r2, rectangle& r); diff --git a/include/nana/gui/msgbox.hpp b/include/nana/gui/msgbox.hpp index af5a1437..e21392e0 100644 --- a/include/nana/gui/msgbox.hpp +++ b/include/nana/gui/msgbox.hpp @@ -12,6 +12,7 @@ #ifndef NANA_GUI_MSGBOX_HPP #define NANA_GUI_MSGBOX_HPP +#include #include @@ -253,5 +254,6 @@ namespace nana ::nana::rectangle valid_areas_[4]; }; }//end namespace nana +#include #endif diff --git a/include/nana/gui/notifier.hpp b/include/nana/gui/notifier.hpp index a557fd45..3e136cdb 100644 --- a/include/nana/gui/notifier.hpp +++ b/include/nana/gui/notifier.hpp @@ -1,7 +1,7 @@ /* * Definition of Notifier * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -14,6 +14,7 @@ #define NANA_GUI_NOTIFIER_HPP #include #include +#include namespace nana { @@ -65,4 +66,5 @@ namespace nana implement * impl_; }; }//end namespace nana +#include #endif diff --git a/include/nana/gui/place.hpp b/include/nana/gui/place.hpp index 8f32545b..4f674cf2 100644 --- a/include/nana/gui/place.hpp +++ b/include/nana/gui/place.hpp @@ -15,6 +15,7 @@ #ifndef NANA_GUI_PLACE_HPP #define NANA_GUI_PLACE_HPP +#include #include #include #include @@ -99,7 +100,7 @@ namespace nana }; public: /// reference to a field manipulator which refers to a field object created by place - typedef field_interface & field_reference; + using field_reference = field_interface &; place(); place(window);///< Attaches to a specified widget. @@ -140,10 +141,11 @@ namespace nana } place& dock(const std::string& dockname, std::string factory_name, std::function(window)> factory); - place& dock_create(const std::string& factory); + widget* dock_create(const std::string& factory); private: implement * impl_; }; }//end namespace nana +#include #endif //#ifndef NANA_GUI_PLACE_HPP diff --git a/include/nana/gui/programming_interface.hpp b/include/nana/gui/programming_interface.hpp index 8adfd2eb..62125e65 100644 --- a/include/nana/gui/programming_interface.hpp +++ b/include/nana/gui/programming_interface.hpp @@ -38,7 +38,7 @@ namespace nana struct widget_traits { using event_type = ::nana::general_events; - using scheme_type = ::nana::widget_colors; + using scheme_type = ::nana::widget_geometrics; }; } @@ -46,7 +46,7 @@ namespace API { namespace detail { - ::nana::widget_colors* make_scheme(::nana::detail::scheme_factory_base&&); + ::nana::widget_geometrics* make_scheme(::nana::detail::scheme_factory_interface&&); } void effects_edge_nimbus(window, effects::edge_nimbus); @@ -60,6 +60,8 @@ namespace API //@brief: The interfaces defined in namespace dev are used for developing the nana.gui namespace dev { + void affinity_execute(window window_handle, const std::function&); + bool set_events(window, const std::shared_ptr&); template @@ -68,8 +70,8 @@ namespace API return std::unique_ptr{static_cast(API::detail::make_scheme(::nana::detail::scheme_factory()))}; } - void set_scheme(window, widget_colors*); - widget_colors* get_scheme(window); + void set_scheme(window, widget_geometrics*); + widget_geometrics* get_scheme(window); void attach_drawer(widget&, drawer_trigger&); ::nana::detail::native_string_type window_caption(window) throw(); @@ -78,8 +80,9 @@ 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); @@ -88,10 +91,20 @@ namespace API void set_menubar(window wd, bool attach); void enable_space_click(window, bool enable); + + /// Refreshs a widget surface + /* + * This function will copy the drawer surface into system window after the event process finished. + */ + void lazy_refresh(); }//end namespace dev - - widget* get_widget(window); + /// Returns the widget pointer of the specified window. + /* + * @param window_handle A handle to a window owning the widget. + * @return A widget pointer. + */ + widget* get_widget(window window_handle); namespace detail { @@ -143,9 +156,17 @@ namespace API }; }//end namespace detail - void exit(); + void exit(); ///< close all windows in current thread + void exit_all(); ///< close all windows - std::string transform_shortkey_text(std::string text, wchar_t &shortkey, std::string::size_type *skpos); + /// @brief Searchs whether the text contains a '&' and removes the character for transforming. + /// If the text contains more than one '&' charachers, the others are ignored. e.g + /// text = "&&a&bcd&ef", the result should be "&abcdef", shortkey = 'b', and pos = 2. + std::string transform_shortkey_text + ( std::string text, ///< the text is transformed + wchar_t &shortkey, ///< the character which indicates a short key. + std::string::size_type *skpos ///< retrives the shortkey position if it is not a null_ptr; + ); bool register_shortkey(window, unsigned long); void unregister_shortkey(window); @@ -181,9 +202,12 @@ 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); @@ -230,7 +254,7 @@ namespace API if (nullptr == wdg_colors) throw std::invalid_argument("API::scheme(): bad parameter window handle, no events object or invalid window handle."); - if (std::is_same<::nana::widget_colors, scheme_type>::value) + if (std::is_same<::nana::widget_geometrics, scheme_type>::value) return *static_cast(wdg_colors); auto * comp_wdg_colors = dynamic_cast(wdg_colors); @@ -258,20 +282,12 @@ namespace API void window_enabled(window, bool); bool window_enabled(window); - /** @brief A widget drawer draws the widget surface in answering an event. - * - * This function will tell the drawer to copy the graphics into window after event answering. - * Tells Nana.GUI to copy the buffer of event window to screen after the event is processed. - * This function only works for a drawer_trigger, when a drawer_trigger receives an event, - * after drawing, a drawer_trigger should call lazy_refresh to tell the Nana.GUI to refresh - * the window to the screen after the event process finished. + /// Refresh the window and display it immediately calling the refresh function of its drawer_trigger. + /* + * The drawer::refresh() will be called. If the currently state is lazy_refrsh, the window is delayed to update the graphics until an event is finished. + * @param window_handle A handle to the window to be refreshed. */ - void lazy_refresh(); - - /** @brief: calls refresh() of a widget's drawer. if currently state is lazy_refresh, Nana.GUI may paste the drawing on the window after an event processing. - * @param window: specify a window to be refreshed. - */ - void refresh_window(window); ///< Refreshs the window and display it immediately calling the refresh method of its drawer_trigger.. + void refresh_window(window window_handle); void refresh_window_tree(window); ///< Refreshs the specified window and all its children windows, then display it immediately void update_window(window); ///< Copies the off-screen buffer to the screen for immediate display. @@ -283,14 +299,36 @@ namespace API cursor window_cursor(window); void activate_window(window); + + /// Determines whether the specified window will get the keyboard focus when its root window gets native system focus. bool is_focus_ready(window); + + /// Returns the current keyboard focus window. window focus_window(); + + /// Sets the keyboard focus for a specified window. void focus_window(window); + /// Returns a window which has grabbed the mouse input. window capture_window(); - window capture_window(window, bool); ///< Enables or disables the window to grab the mouse input - void capture_ignore_children(bool ignore); ///< Enables or disables the captured window whether redirects the mouse input to its children if the mouse is over its children. - void modal_window(window); ///< Blocks the routine til the specified window is closed. + + /// Enables a window to grab the mouse input. + /** + * @param window_handle A handle to a window to grab the mouse input. + * @param ignore_children Indicates whether to redirect the mouse input to its children if the mouse pointer is over its children. + */ + void set_capture(window window_handle, bool ignore_children); + + /// Disable a window to grab the mouse input. + /** + * @param window handle A handle to a window to release grab of mouse input. + */ + void release_capture(window window_handle); + + /// Blocks the execution and other windows' messages until the specified window is closed. + void modal_window(window); + + /// Blocks the execution until the specified window is closesd. void wait_for(window); color fgcolor(window); @@ -300,20 +338,38 @@ namespace API color activated_color(window); color activated_color(window, const color&); - void create_caret(window, unsigned width, unsigned height); + void create_caret(window, const size&); void destroy_caret(window); - void caret_effective_range(window, const rectangle&); - void caret_pos(window, const ::nana::point&); - nana::point caret_pos(window); - nana::size caret_size(window); - void caret_size(window, const size&); - void caret_visible(window, bool is_show); - bool caret_visible(window); - void tabstop(window); ///< Sets the window that owns the tabstop. - /// treu: The focus is not to be changed when Tab key is pressed, and a key_char event with tab will be generated. - void eat_tabstop(window, bool); - window move_tabstop(window, bool next); ///< Sets the focus to the window which tabstop is near to the specified window. + /// Opens an existing caret of a window. + /** + * This function returns an object to operate caret. The object doesn't create or destroy the caret. + * When you are finished with the caret, be sure to reset the pointer. + * + * @param window_handle A handle to a window whose caret is to be retrieved + * @return a pointer to the caret proxy. nullptr if the window doesn't have a caret. + */ + ::std::unique_ptr open_caret(window window_handle, bool disable_throw = false); + + /// Enables that the user can give input focus to the specified window using TAB key. + void tabstop(window); + + /// Enables or disables a window to receive a key_char event for pressing TAB key. + /* + * @param window_handle A handle to the window to catch TAB key through key_char event. + * @param enable Indicates whether to enable or disable catch of TAB key. If this parameter is *true*, the window is + * received a key_char when pressing TAB key, and the input focus is not changed. If this parameter is *false*, the + * input focus is changed to the next tabstop window. + */ + void eat_tabstop(window window_handle, bool enable); + + /// Sets the input focus to the window which the tabstop is near to the specified window. + /* + * @param window_handle A handle to the window. + * @param forward Indicates whether forward or backward window to be given the input focus. + * @return A handle to the window which to be given the input focus. + */ + window move_tabstop(window window_handle, bool forward); /// 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); @@ -343,6 +399,7 @@ namespace API void at_safe_place(window, std::function); }//end namespace API + }//end namespace nana #endif diff --git a/include/nana/gui/timer.hpp b/include/nana/gui/timer.hpp index 893a4e5d..9ae26f32 100644 --- a/include/nana/gui/timer.hpp +++ b/include/nana/gui/timer.hpp @@ -16,6 +16,7 @@ #ifndef NANA_GUI_TIMER_HPP #define NANA_GUI_TIMER_HPP #include +#include namespace nana { @@ -58,4 +59,5 @@ namespace nana implement * const impl_; }; }//end namespace nana +#include #endif diff --git a/include/nana/gui/widgets/button.hpp b/include/nana/gui/widgets/button.hpp index bddeca77..e1813917 100644 --- a/include/nana/gui/widgets/button.hpp +++ b/include/nana/gui/widgets/button.hpp @@ -14,6 +14,8 @@ #define NANA_GUI_WIDGET_BUTTON_HPP #include "widget.hpp" #include +#include + namespace nana{ namespace drawerbase @@ -105,5 +107,7 @@ namespace nana{ void _m_caption(native_string_type&&) override; }; }//end namespace nana +#include + #endif diff --git a/include/nana/gui/widgets/categorize.hpp b/include/nana/gui/widgets/categorize.hpp index 3b8f5b3c..1e64fa14 100644 --- a/include/nana/gui/widgets/categorize.hpp +++ b/include/nana/gui/widgets/categorize.hpp @@ -16,6 +16,7 @@ #include #include #include +#include namespace nana { @@ -66,7 +67,7 @@ namespace nana T null_val; arg_categorize arg(widget_, vp ? *vp : null_val); - widget_.events().selected.emit(arg); + widget_.events().selected.emit(arg, widget_.handle()); } private: ::nana::categorize & widget_; @@ -141,7 +142,7 @@ namespace nana void mouse_leave(graph_reference, const arg_mouse&) override; private: std::unique_ptr event_agent_; - scheme * scheme_; + scheme * scheme_{nullptr}; }; }//end namespace categorize }//end namespace drawerbase @@ -258,5 +259,5 @@ namespace nana } }; }//end namespace nana - +#include #endif diff --git a/include/nana/gui/widgets/checkbox.hpp b/include/nana/gui/widgets/checkbox.hpp index 8d458be3..8d06b590 100644 --- a/include/nana/gui/widgets/checkbox.hpp +++ b/include/nana/gui/widgets/checkbox.hpp @@ -12,6 +12,8 @@ #ifndef NANA_GUI_WIDGET_CHECKBOX_HPP #define NANA_GUI_WIDGET_CHECKBOX_HPP +#include + #include "widget.hpp" #include #include @@ -106,5 +108,6 @@ namespace drawerbase std::vector ui_container_; }; }//end namespace nana +#include #endif diff --git a/include/nana/gui/widgets/combox.hpp b/include/nana/gui/widgets/combox.hpp index 4d22a30e..7e478557 100644 --- a/include/nana/gui/widgets/combox.hpp +++ b/include/nana/gui/widgets/combox.hpp @@ -12,6 +12,7 @@ #ifndef NANA_GUI_WIDGETS_COMBOX_HPP #define NANA_GUI_WIDGETS_COMBOX_HPP +#include #include "widget.hpp" #include "float_listbox.hpp" #include "skeletons/text_editor_part.hpp" @@ -227,4 +228,5 @@ namespace nana nana::any * _m_anyobj(std::size_t pos, bool alloc_if_empty) const override; }; } +#include #endif diff --git a/include/nana/gui/widgets/date_chooser.hpp b/include/nana/gui/widgets/date_chooser.hpp index a4e685e1..b36d800f 100644 --- a/include/nana/gui/widgets/date_chooser.hpp +++ b/include/nana/gui/widgets/date_chooser.hpp @@ -1,7 +1,7 @@ /** * A date chooser Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -12,86 +12,53 @@ #ifndef NANA_GUI_WIDGETS_DATE_CHOOSER_HPP #define NANA_GUI_WIDGETS_DATE_CHOOSER_HPP - +#include #include "widget.hpp" #include namespace nana { + class date_chooser; + + struct arg_datechooser + : public event_arg + { + date_chooser * const widget; + + arg_datechooser(date_chooser* wdg) + : widget(wdg) + {} + }; + namespace drawerbase { namespace date_chooser { + struct date_chooser_events + : public general_events + { + + basic_event date_changed; + }; + class trigger : public drawer_trigger { + class model; public: static const int topbar_height = 34; static const int border_size = 3; - enum class transform_action{none, to_left, to_right, to_enter, to_leave}; - enum class where{none, left_button, right_button, topbar, textarea}; - enum class page{date, month}; - - struct drawing_basis - { - nana::point refpos; - double line_s; - double row_s; - }; - trigger(); - bool chose() const; - nana::date read() const; - void week_name(unsigned index, const std::string&); - private: - where _m_pos_where(graph_reference, const ::nana::point& pos); - void _m_draw_topbar(graph_reference); - void _m_make_drawing_basis(drawing_basis&, graph_reference, const nana::point& refpos); - void _m_draw_pos(drawing_basis &, graph_reference, int x, int y, const ::std::string&, bool primary, bool sel); - void _m_draw_pos(drawing_basis &, graph_reference, int x, int y, int number, bool primary, bool sel); - void _m_draw_ex_days(drawing_basis &, graph_reference, int begx, int begy, bool before); - void _m_draw_days(const nana::point& refpos, graph_reference); - void _m_draw_months(const nana::point& refpos, graph_reference); - bool _m_get_trace(point, int & res); - void _m_perf_transform(transform_action tfid, graph_reference, graph_reference dirtybuf, graph_reference newbuf, const nana::point& refpos); + ~trigger(); + model* get_model() const; private: void refresh(graph_reference) override; - void attached(widget_reference, graph_reference) override; void mouse_move(graph_reference, const arg_mouse&) override; void mouse_leave(graph_reference, const arg_mouse&) override; void mouse_up(graph_reference, const arg_mouse&) override; + void key_press(graph_reference, const arg_keyboard&) override; private: - ::std::string weekstr_[7]; - - widget * widget_; - - bool chose_; - page page_; - where pos_; - nana::point trace_pos_; - - drawing_basis dbasis_; - - struct - { - int year; - int month; - int day; - }chdate_; - - struct - { - int year; - int month; - }chmonth_; - - struct color_tag - { - ::nana::color highlight; - ::nana::color selected; - ::nana::color normal; - ::nana::color bgcolor; - }color_; + model * model_; }; }//end namespace date_chooser @@ -100,19 +67,18 @@ namespace nana /// \see nana::date class date_chooser - : public widget_object + : public widget_object { public: date_chooser(); date_chooser(window, bool visible); - date_chooser(window, const ::std::string& text, bool visible = true); - date_chooser(window, const char* text, bool visible = true); date_chooser(window, const nana::rectangle& r = rectangle(), bool visible = true); bool chose() const; nana::date read() const; - void weekstr(unsigned index, const ::std::string&);/// #endif diff --git a/include/nana/gui/widgets/detail/tree_cont.hpp b/include/nana/gui/widgets/detail/tree_cont.hpp index 3618b814..c9684ba6 100644 --- a/include/nana/gui/widgets/detail/tree_cont.hpp +++ b/include/nana/gui/widgets/detail/tree_cont.hpp @@ -12,6 +12,7 @@ #ifndef NANA_GUI_WIDGETS_DETAIL_TREE_CONT_HPP #define NANA_GUI_WIDGETS_DETAIL_TREE_CONT_HPP #include +#include namespace nana { @@ -515,4 +516,6 @@ namespace detail }//end namespace detail }//end namespace widgets }//end namesace nana + +#include #endif diff --git a/include/nana/gui/widgets/float_listbox.hpp b/include/nana/gui/widgets/float_listbox.hpp index 79a66e2f..590479fd 100644 --- a/include/nana/gui/widgets/float_listbox.hpp +++ b/include/nana/gui/widgets/float_listbox.hpp @@ -12,6 +12,7 @@ #ifndef NANA_GUI_WIDGETS_FLOAT_LISTBOX_HPP #define NANA_GUI_WIDGETS_FLOAT_LISTBOX_HPP +#include #include "widget.hpp" #include @@ -101,5 +102,6 @@ namespace nana std::size_t index() const; }; } +#include #endif diff --git a/include/nana/gui/widgets/form.hpp b/include/nana/gui/widgets/form.hpp index 66047576..77972a8b 100644 --- a/include/nana/gui/widgets/form.hpp +++ b/include/nana/gui/widgets/form.hpp @@ -14,9 +14,12 @@ #define NANA_GUI_WIDGET_FORM_HPP #include "widget.hpp" +#include namespace nana { + class place; + namespace drawerbase { namespace form @@ -29,12 +32,29 @@ namespace nana private: widget* wd_{nullptr}; }; + + class form_base + : public widget_object + { + public: + form_base(window owner, bool nested, const rectangle&, const appearance&); + + //place methods + + place & get_place(); + void div(const char* div_text); + place::field_reference operator[](const char* field_name); + void collocate() noexcept; + private: + std::unique_ptr place_; + }; }//end namespace form }//end namespace drawerbase /// \brief Pop-up window. Is different from other window widgets: its default constructor create the window. /// \see nana::appearance - class form: public widget_object + class form + : public drawerbase::form::form_base { public: using appear = ::nana::appear; @@ -50,7 +70,8 @@ namespace nana void wait_for_this(); }; - class nested_form : public widget_object + class nested_form + : public drawerbase::form::form_base { public: using appear = ::nana::appear; diff --git a/include/nana/gui/widgets/frame.hpp b/include/nana/gui/widgets/frame.hpp index 6cc91dce..82a53d95 100644 --- a/include/nana/gui/widgets/frame.hpp +++ b/include/nana/gui/widgets/frame.hpp @@ -16,6 +16,8 @@ #define NANA_GUI_WIDGET_FRAME_HPP #include "widget.hpp" + +#ifndef WIDGET_FRAME_DEPRECATED namespace nana { /** @@ -51,3 +53,4 @@ namespace nana }; }//end namespace nana #endif +#endif diff --git a/include/nana/gui/widgets/label.hpp b/include/nana/gui/widgets/label.hpp index 6b71bd88..f8c97eb9 100644 --- a/include/nana/gui/widgets/label.hpp +++ b/include/nana/gui/widgets/label.hpp @@ -12,8 +12,9 @@ #ifndef NANA_GUI_WIDGET_LABEL_HPP #define NANA_GUI_WIDGET_LABEL_HPP -#include "widget.hpp" +#include "widget.hpp" +#include namespace nana { @@ -80,4 +81,6 @@ namespace nana void _m_caption(native_string_type&&) override; }; }//end namespace nana +#include + #endif diff --git a/include/nana/gui/widgets/listbox.hpp b/include/nana/gui/widgets/listbox.hpp index 58f02ef0..e4b06724 100644 --- a/include/nana/gui/widgets/listbox.hpp +++ b/include/nana/gui/widgets/listbox.hpp @@ -11,12 +11,15 @@ * @contributors: * Hiroshi Seki * Ariel Vina-Rodriguez - * leobackes - * Benjamin Navarro + * leobackes(pr#86,pr#97) + * Benjamin Navarro(pr#81) + * besh81(pr#130) */ #ifndef NANA_GUI_WIDGETS_LISTBOX_HPP #define NANA_GUI_WIDGETS_LISTBOX_HPP +#include + #include "widget.hpp" #include "detail/inline_widget.hpp" #include @@ -24,6 +27,8 @@ #include #include #include +#include +#include namespace nana { @@ -33,6 +38,592 @@ namespace nana { namespace listbox { + /// An interface of column operations + class column_interface + { + public: + /// Destructor + virtual ~column_interface() = default; + + /// Returns the width of column, in pixel + virtual unsigned width() const noexcept = 0; + + /// Sets width + /** + * @param pixels The pixels of width + */ + virtual void width(unsigned pixels) noexcept = 0; + + /// Automatically adjusted width + /** + * @param minimum The minimal width of column, in pixel + * @param maximum The maximal width of column, in pixel + */ + virtual void width(unsigned minimum, unsigned maximum) = 0; + + /// Sets alignment of column text + /** + * @param align Alignment + */ + virtual void text_align(::nana::align align) noexcept = 0; + + /// Adjusts the width to fit the content + /** + * The priority of max: maximum, ranged width, scheme's max_fit_content. + * @param maximum Sets the width of column to the maximum if the width of content is larger than maximum + */ + virtual void fit_content(unsigned maximum = 0) noexcept = 0; + + /// Determines the visibility state of the column + /** + * @return true if the column is visible, false otherwise + */ + virtual bool visible() const noexcept = 0; + + /// Sets the visibility state of the column + /** + * @param is_visible Indicates whether to show or hide the column + */ + virtual void visible(bool is_visible) noexcept = 0; + }; + + class const_virtual_pointer + { + struct intern + { + public: + virtual ~intern() = default; + }; + + template + struct real_pointer + : public intern + { + const T * ptr; + + real_pointer(const T* p) + : ptr(p) + {} + }; + + const_virtual_pointer(const const_virtual_pointer&) = delete; + const_virtual_pointer& operator=(const const_virtual_pointer&) = delete; + + const_virtual_pointer(const_virtual_pointer&&) = delete; + const_virtual_pointer& operator=(const_virtual_pointer&&) = delete; + public: + template + explicit const_virtual_pointer(const Type* p) + : intern_(new real_pointer{p}) + { + } + + ~const_virtual_pointer() + { + delete intern_; + } + + template + const typename std::remove_const::type *get() const + { + using value_type = typename std::remove_const::type; + auto target = dynamic_cast*>(intern_); + return (target ? target->ptr : nullptr); + } + private: + intern * intern_; + }; + + struct cell + { + struct format + { + ::nana::color bgcolor; + ::nana::color fgcolor; + + format() = default; + format(const ::nana::color& bgcolor, const ::nana::color& fgcolor); + }; + + using format_ptr = ::std::unique_ptr; + + ::std::string text; + format_ptr custom_format; + + cell() = default; + cell(const cell&); + cell(cell&&); + cell(::std::string); + cell(::std::string, const format&); + cell(::std::string, const ::nana::color& bgcolor, const ::nana::color& fgcolor); + + cell& operator=(const cell&); + cell& operator=(cell&&); + }; + + class container_interface + { + friend class model_guard; + public: + virtual ~container_interface() = default; + + virtual void clear() = 0; + virtual void erase(std::size_t pos) = 0; + + virtual std::size_t size() const = 0; + virtual bool immutable() const = 0; + + virtual void emplace(std::size_t pos) = 0; + virtual void emplace_back() = 0; + + virtual void assign(std::size_t pos, const std::vector& cells) = 0; + virtual std::vector to_cells(std::size_t pos) const = 0; + + virtual bool push_back(const const_virtual_pointer&) = 0; + + virtual void * pointer() = 0; + virtual const void* pointer() const = 0; + }; + + template + struct container_translator + { + using value_translator = std::function& cells)>; + using cell_translator = std::function(const Value&)>; + + value_translator to_value; + cell_translator to_cell; + }; + + template + class basic_container + : public container_interface + { + }; + + template + class standalone_container + : public basic_container + { + using value_type = typename STLContainer::value_type; + using value_translator = typename container_translator::value_translator; + using cell_translator = typename container_translator::cell_translator; + public: + standalone_container(STLContainer&& cont, value_translator vtrans, cell_translator ctrans) + : container_(std::move(cont)), + translator_({ vtrans, ctrans }) + {} + + standalone_container(const STLContainer& cont, value_translator vtrans, cell_translator ctrans) + : container_(cont), + translator_({ vtrans, ctrans }) + {} + private: + void clear() override + { + container_.clear(); + } + + void erase(std::size_t pos) override + { + auto i = container_.begin(); + std::advance(i, static_cast(pos)); + container_.erase(i); + } + + std::size_t size() const override + { + return container_.size(); + } + + bool immutable() const override + { + return false; + } + + void emplace(std::size_t pos) override + { + auto i = container_.begin(); + std::advance(i, static_cast(pos)); + + container_.emplace(i); + } + + void emplace_back() override + { + container_.emplace_back(); + } + + void assign(std::size_t pos, const std::vector& cells) override + { + container_.at(pos) = translator_.to_value(cells); + } + + std::vector to_cells(std::size_t pos) const override + { + return translator_.to_cell(container_.at(pos)); + } + + bool push_back(const const_virtual_pointer& dptr) override + { + auto value = dptr.get(); + if (value) + { + container_.push_back(*value); + return true; + } + return false; + } + + void* pointer() override + { + return &container_; + } + + const void* pointer() const override + { + return &container_; + } + private: + STLContainer container_; + container_translator translator_; + }; + + + template + class shared_container + : public basic_container + { + using value_type = typename STLContainer::value_type; + using value_translator = typename container_translator::value_translator; + using cell_translator = typename container_translator::cell_translator; + + public: + using container_reference = STLContainer&; + + + shared_container(container_reference cont, value_translator vtrans, cell_translator ctrans) + : container_(cont), translator_({ vtrans, ctrans }) + { + + } + private: + void clear() override + { + container_.clear(); + } + + void erase(std::size_t pos) override + { + auto i = container_.begin(); + std::advance(i, static_cast(pos)); + container_.erase(i); + } + + std::size_t size() const override + { + return container_.size(); + } + + bool immutable() const override + { + return false; + } + + void emplace(std::size_t pos) override + { + auto i = container_.begin(); + std::advance(i, static_cast(pos)); + + container_.emplace(i); + } + + void emplace_back() override + { + container_.emplace_back(); + } + + void assign(std::size_t pos, const std::vector& cells) override + { + container_.at(pos) = translator_.to_value(cells); + } + + std::vector to_cells(std::size_t pos) const override + { + return translator_.to_cell(container_.at(pos)); + } + + bool push_back(const const_virtual_pointer& dptr) override + { + auto value = dptr.get(); + if (value) + { + container_.push_back(*value); + return true; + } + return false; + } + + void* pointer() override + { + return &container_; + } + + const void* pointer() const override + { + return &container_; + } + private: + container_reference container_; + container_translator translator_; + }; + + template + class shared_immutable_container + : public basic_container + { + using value_type = typename STLContainer::value_type; + using cell_translator = typename container_translator::cell_translator; + + + public: + using container_reference = const STLContainer&; + + + shared_immutable_container(container_reference cont, cell_translator ctrans) + : container_(cont), ctrans_(ctrans) + { + } + private: + void clear() override + { + throw std::runtime_error("nana::listbox disallow to remove items because of immutable model"); + } + + void erase(std::size_t pos) override + { + throw std::runtime_error("nana::listbox disallow to remove items because of immutable model"); + } + + std::size_t size() const override + { + return container_.size(); + } + + bool immutable() const override + { + return true; + } + + void emplace(std::size_t pos) override + { + throw std::runtime_error("nana::listbox disallow to remove items because of immutable model"); + } + + void emplace_back() override + { + throw std::runtime_error("nana::listbox disallow to remove items because of immutable model"); + } + + void assign(std::size_t pos, const std::vector& cells) override + { + throw std::runtime_error("nana::listbox disallow to remove items because of immutable model"); + } + + std::vector to_cells(std::size_t pos) const override + { + return ctrans_(container_.at(pos)); + } + + bool push_back(const const_virtual_pointer& dptr) override + { + throw std::runtime_error("nana::listbox disallow to remove items because of immutable model"); + } + + void* pointer() override + { + return nullptr; + } + + const void* pointer() const override + { + return &container_; + } + private: + container_reference container_; + cell_translator ctrans_; + }; + + class model_interface + { + public: + virtual ~model_interface() = default; + + virtual void lock() = 0; + virtual void unlock() = 0; + + virtual container_interface* container() = 0; + virtual const container_interface* container() const = 0; + }; + + class model_guard + { + model_guard(const model_guard&) = delete; + model_guard& operator=(const model_guard&) = delete; + public: + model_guard(model_interface* model) + : model_(model) + { + model->lock(); + } + + model_guard(model_guard&& other) + : model_(other.model_) + { + other.model_ = nullptr; + } + + ~model_guard() noexcept + { + if (model_) + model_->unlock(); + } + + model_guard& operator=(model_guard&& other) + { + if (this != &other) + { + if (model_) + model_->unlock(); + + model_ = other.model_; + other.model_ = nullptr; + } + return *this; + } + + template + STLContainer& container() + { + using stlcontainer = typename std::decay::type; + + if (!model_) + throw std::runtime_error("nana::listbox empty model_guard"); + + using type = basic_container; + auto p = dynamic_cast(model_->container()); + if (nullptr == p) + throw std::invalid_argument("invalid listbox model container type"); + + if (nullptr == p->pointer()) + throw std::runtime_error("the modal is immutable"); + + return *static_cast(p->pointer()); + } + + template + const STLContainer& container() const + { + using stlcontainer = typename std::decay::type; + + if (!model_) + throw std::runtime_error("nana::listbox empty model_guard"); + + using type = basic_container; + auto p = dynamic_cast(model_->container()); + if (nullptr == p) + throw std::invalid_argument("invalid listbox model container type"); + + return *static_cast(p->pointer()); + } + private: + model_interface* model_; + }; + + template + class standalone_model_container + : public model_interface + { + public: + using value_translator = typename container_translator::value_translator; + using cell_translator = typename container_translator::cell_translator; + + standalone_model_container(STLContainer&& container, value_translator vtrans, cell_translator ctrans) + : container_(std::move(container), std::move(vtrans), std::move(ctrans)) + { + } + + standalone_model_container(const STLContainer& container, value_translator vtrans, cell_translator ctrans) + : container_(container, std::move(vtrans), std::move(ctrans)) + { + } + + void lock() override + { + mutex_.lock(); + } + + void unlock() override + { + mutex_.unlock(); + } + + container_interface* container() override + { + return &container_; + } + + const container_interface* container() const override + { + return &container_; + } + private: + Mutex mutex_; + standalone_container container_; + }; + + template + class shared_model_container + : public model_interface + { + public: + using value_translator = typename container_translator::value_translator; + using cell_translator = typename container_translator::cell_translator; + + shared_model_container(STLContainer& container, value_translator vtrans, cell_translator ctrans) + : container_ptr_(new shared_container(container, std::move(vtrans), std::move(ctrans))) + { + } + + shared_model_container(const STLContainer& container, cell_translator ctrans) + : container_ptr_(new shared_immutable_container(container, std::move(ctrans))) + { + } + + void lock() override + { + mutex_.lock(); + } + + void unlock() override + { + mutex_.unlock(); + } + + container_interface* container() override + { + return container_ptr_.get(); + } + + const container_interface* container() const override + { + return container_ptr_.get(); + } + private: + Mutex mutex_; + std::unique_ptr container_ptr_; + }; + using size_type = std::size_t; using native_string_type = ::nana::detail::native_string_type; @@ -42,7 +633,7 @@ namespace nana size_type cat; //The pos of category size_type item; //the pos of item in a category. - index_pair(size_type cat_pos = 0, size_type item_pos = 0) + explicit index_pair(size_type cat_pos = 0, size_type item_pos = 0) : cat(cat_pos), item(item_pos) {} @@ -83,40 +674,20 @@ namespace nana } }; - using selection = std::vector; + using index_pairs = ::std::vector; - using inline_notifier_interface = detail::inline_widget_notifier_interface; + using inline_notifier_interface = detail::inline_widget_notifier_interface; - struct cell - { - struct format - { - ::nana::color bgcolor; - ::nana::color fgcolor; - /// ::nana::paint::font font; \todo - format() = default; - format(const ::nana::color& bgcolor, const ::nana::color& fgcolor); - }; - using format_ptr = ::std::unique_ptr; - - ::std::string text; - format_ptr custom_format; - - cell() = default; - cell(const cell&); - cell(cell&&); - cell(::std::string); - cell(::std::string, const format&); - cell(::std::string, const ::nana::color& bgcolor, const ::nana::color& fgcolor); - - cell& operator=(const cell&); - cell& operator=(cell&&); - }; + // struct essence + //@brief: this struct gives many data for listbox, + // the state of the struct does not effect on member funcions, therefore all data members are public. + struct essence; class oresolver { public: + oresolver(essence*); oresolver& operator<<(bool); oresolver& operator<<(short); oresolver& operator<<(unsigned short); @@ -139,7 +710,10 @@ namespace nana oresolver& operator<<(std::nullptr_t); std::vector && move_cells(); + + ::nana::listbox& listbox(); private: + essence* const ess_; std::vector cells_; }; @@ -170,12 +744,6 @@ namespace nana std::size_t pos_{0}; }; - using selection = std::vector; - - /// struct essence_t - ///@brief: this struct gives many data for listbox, - /// the state of the struct does not effect on member funcions, therefore all data members are public. - struct essence_t; struct category_t; class drawer_header_impl; @@ -187,7 +755,7 @@ namespace nana public: trigger(); ~trigger(); - essence_t& essence() const; + essence& ess() const; private: void _m_draw_border(); private: @@ -205,7 +773,7 @@ namespace nana void key_press(graph_reference, const arg_keyboard&) override; void key_char(graph_reference, const arg_keyboard&) override; private: - essence_t * essence_; + essence * essence_; drawer_header_impl *drawer_header_; drawer_lister_impl *drawer_lister_; };//end class trigger @@ -216,15 +784,15 @@ namespace nana : public std::iterator { public: - item_proxy(essence_t*); - item_proxy(essence_t*, const index_pair&); + item_proxy(essence*); + item_proxy(essence*, const index_pair&); - /// the main porpose of this it to make obvious that item_proxy operate with absolute positions, and dont get moved during sort() - static item_proxy from_display(essence_t *ess, const index_pair &relative) ; - item_proxy from_display(const index_pair &relative) const; + /// the main porpose of this it to make obvious that item_proxy operate with absolute positions, and dont get moved during sort() + static item_proxy from_display(essence *, const index_pair &relative) ; + item_proxy from_display(const index_pair &relative) const; - /// posible use: last_selected_display = last_selected.to_display().item; use with caution, it get invalidated after a sort() - index_pair to_display() const; + /// posible use: last_selected_display = last_selected.to_display().item; use with caution, it get invalidated after a sort() + index_pair to_display() const; bool empty() const; @@ -246,7 +814,7 @@ namespace nana item_proxy& text(size_type col, cell); item_proxy& text(size_type col, std::string); - item_proxy& text(size_type col, std::wstring); + item_proxy& text(size_type col, const std::wstring&); std::string text(size_type col) const; void icon(const nana::paint::image&); @@ -254,7 +822,7 @@ namespace nana template item_proxy & resolve_from(const T& t) { - oresolver ores; + oresolver ores(_m_ess()); ores << t; auto && cells = ores.move_cells(); auto cols = columns(); @@ -295,7 +863,18 @@ namespace nana throw std::runtime_error("listbox::item_proxy.value() invalid type of value"); return *p; } + template + T & value() + { + auto * pany = _m_value(); + if (nullptr == pany) + throw std::runtime_error("listbox::item_proxy.value() is empty"); + T * p = any_cast(_m_value(false)); + if (nullptr == p) + throw std::runtime_error("listbox::item_proxy.value() invalid type of value"); + return *p; + } template item_proxy & value(T&& t) { @@ -337,15 +916,16 @@ namespace nana bool operator!=(const item_proxy&) const; //Undocumented method - essence_t * _m_ess() const; + essence * _m_ess() const; private: std::vector & _m_cells() const; - nana::any * _m_value(bool alloc_if_empty); - const nana::any * _m_value() const; + nana::any * _m_value(bool alloc_if_empty); + const nana::any * _m_value() const; private: - essence_t * ess_; + essence * ess_; category_t* cat_{nullptr}; - index_pair pos_; + + index_pair pos_; //Position of an item, it never represents a category when item proxy is available. }; class cat_proxy @@ -353,16 +933,22 @@ namespace nana { public: using inline_notifier_interface = drawerbase::listbox::inline_notifier_interface; + template using value_translator = typename container_translator::value_translator; + template using cell_translator = typename container_translator::cell_translator; cat_proxy() = default; - cat_proxy(essence_t*, size_type pos); - cat_proxy(essence_t*, category_t*); + cat_proxy(essence*, size_type pos); + cat_proxy(essence*, category_t*); /// Append an item at abs end of the category, set_value determines whether assign T object to the value of item. template item_proxy append(T&& t, bool set_value = false) { - oresolver ores; + oresolver ores(ess_); + + //Troubleshoot: + //If a compiler error that no operator<< overload found for type T occurs, please define a overload operator<<(oresolver&, const T&). + //If a listbox have a model set, try call append_model instead. if (set_value) ores << t; //copy it if it is rvalue and set_value is true. else @@ -379,6 +965,33 @@ namespace nana return iter; } + template + void append_model(const T& t) + { + _m_try_append_model(const_virtual_pointer{ &t }); + _m_update(); + } + + template + void model(STLContainer&& container, ValueTranslator vtrans, CellTranslator ctrans) + { + _m_reset_model(new standalone_model_container::type, Mutex>(std::forward(container), std::move(vtrans), std::move(ctrans))); + } + + template + void shared_model(STLContainer& container, ValueTranslator vtrans, CellTranslator ctrans) + { + _m_reset_model(new shared_model_container::type, Mutex>(container, std::move(vtrans), std::move(ctrans))); + } + + template + void shared_model(const STLContainer& container, CellTranslator ctrans) + { + _m_reset_model(new shared_model_container::type, Mutex>(container, std::move(ctrans))); + } + + model_guard model(); + /// Appends one item at the end of this category with the specifies text in the column fields void append(std::initializer_list texts_utf8); void append(std::initializer_list texts); @@ -407,11 +1020,11 @@ namespace nana /// convert from display order to absolute (find the real item in that display pos) but without check from current active sorting, in fact using just the last sorting !!! size_type index_by_display_order(size_type disp_order) const; - /// find display order for the real item but without check from current active sorting, in fact using just the last sorting !!! - size_type display_order(size_type pos) const; + /// find display order for the real item but without check from current active sorting, in fact using just the last sorting !!! + size_type display_order(size_type pos) const; - /// this cat position - size_type position() const; + /// this cat position + size_type position() const; /// Returns the number of items size_type size() const; @@ -446,10 +1059,12 @@ namespace nana void inline_factory(size_type column, pat::cloneable> factory); private: void _m_append(std::vector && cells); + void _m_try_append_model(const const_virtual_pointer&); void _m_cat_by_pos(); void _m_update(); + void _m_reset_model(model_interface*); private: - essence_t* ess_{nullptr}; + essence* ess_{nullptr}; category_t* cat_{nullptr}; size_type pos_{0}; ///< Absolute position, not relative to display, and dont change during sort() }; @@ -468,30 +1083,24 @@ namespace nana } }//end namespace drawerbase - struct arg_listbox - : public event_arg - { - mutable drawerbase::listbox::item_proxy item; - bool selected; - - arg_listbox(const drawerbase::listbox::item_proxy&, bool selected) noexcept; - }; - - /// The event argument type for listbox's category_dbl_click - struct arg_listbox_category + struct arg_listbox : public event_arg { - drawerbase::listbox::cat_proxy category; + mutable drawerbase::listbox::item_proxy item; - /// Block expension/shrink of category - void block_category_change() const noexcept; + arg_listbox(const drawerbase::listbox::item_proxy&) noexcept; + }; - /// Determines whether expension/shrink of category is blocked - bool category_change_blocked() const noexcept; + /// The event parameter type for listbox's category_dbl_click + struct arg_listbox_category + : public event_arg + { + drawerbase::listbox::cat_proxy category; + + /// A flag that indicates whether or not to block expension/shrink of category when it is double clicking. + mutable bool block_operation{ false }; arg_listbox_category(const drawerbase::listbox::cat_proxy&) noexcept; - private: - mutable bool block_change_; }; namespace drawerbase @@ -501,41 +1110,61 @@ namespace nana struct listbox_events : public general_events { + /// An envent occurs when the toggle of a listbox item is checked. basic_event checked; + + /// An event occurs when a listbox item is clicked. basic_event selected; /// An event occurs when a listbox category is double clicking. - basic_event category_dbl_click; + basic_event category_dbl_click; }; struct scheme - : public widget_colors + : public widget_geometrics { color_proxy header_bgcolor{static_cast(0xf1f2f4)}; color_proxy header_grabbed{ static_cast(0x8BD6F6)}; color_proxy header_floated{ static_cast(0xBABBBC)}; color_proxy item_selected{ static_cast(0xD5EFFC) }; - unsigned max_header_width{3000}, /// \todo how to implement some geometrical parameters ?? - ext_w = 5; + /// The max column width which is generated by fit_content is allowed. It is ignored when it is 0, or a max value is passed to fit_content. + unsigned max_fit_content{ 0 }; + + unsigned min_column_width{ 20 }; ///< def=20 . non counting suspension_width + + unsigned suspension_width { 8 }; ///< def= . the trigger will set this to the width if ("...") + unsigned text_margin { 5 }; ///< def= 5. Additional or extended with added (before) to the text width to determine the cell width. cell_w = text_w + ext_w +1 + unsigned header_height { 25 }; ///< def=25 . header height header_size + unsigned text_height { 14 }; ///< the trigger will set this to the height of the text font + unsigned item_height_ex { 6 }; ///< Set !=0 !!!! def=6. item_height = text_height + item_height_ex + unsigned item_height { 24 }; ///< def=24 . the trigger will set this TO item_height = text_height + item_height_ex + unsigned header_splitter_area_before{ 2 }; ///< def=2. But 4 is better... IMO + unsigned header_splitter_area_after { 3 }; ///< def=3. But 4 is better... + + ::nana::parameters::mouse_wheel mouse_wheel{}; ///< The number of lines/characters to scroll when vertical/horizontal mouse wheel is moved. }; } }//end namespace drawerbase /*! \class listbox -\brief A rectangle containing a list of strings from which the user can select. This widget contain a list of \a categories, with in turn contain a list of \a items. -A category is a text with can be \a selected, \a checked and \a expanded to show the items. -An item is formed by \a column-fields, each corresponding to one of the \a headers. -An item can be \a selected and \a checked. +\brief A rectangle containing a list of strings from which the user can select. +This widget contain a list of \a categories, with in turn contain a list of \a items. +A \a category is a text with can be \a selected, \a checked and \a expanded to show the \a items. +An \a item is formed by \a column-fields, each corresponding to one of the \a headers. +An \a item can be \a selected and \a checked. The user can \a drag the header to \a resize it or to \a reorganize it. By \a clicking on one header the list get \a reordered, first up, and then down alternatively. -1. The resolver is used to resolute an object of the specified type for a listbox item. -3. nana::listbox creates the category 0 by default. The member functions without the categ parameter operate the items that belong to category 0. +1. The resolver is used to resolute an object of the specified type into (or back from) a listbox item. +3. nana::listbox creates the category 0 by default. + This is an special category, becouse it is invisible, while the associated items are visible. + The optional, user-created categories begin at index 1 and are visibles. + The member functions without the categ parameter operate the items that belong to category 0. 4. A sort compare is used for sorting the items. It is a strict weak ordering comparer that must meet the requirement: Irreflexivity (comp(x, x) returns false) and - antisymmetry(comp(a, b) != comp(b, a) returns true) + Antisymmetry(comp(a, b) != comp(b, a) returns true) A simple example. bool sort_compare( const std::string& s1, nana::any*, const std::string& s2, nana::any*, bool reverse) @@ -550,15 +1179,26 @@ By \a clicking on one header the list get \a reordered, first up, and then down { if(o1 && o2) //some items may not attach a customer object. { - int * i1 = o1->get(); - int * i2 = o2->get(); + int * i1 = any_cast(*o1); + int * i2 = any_cast(*o2); return (i1 && i2 && (reverse ? *i1 > *i2 : *i1 < *i2)); - ;//some types may not be int. + // ^ some types may not be int. } return false; } listbox.anyobj(0, 0, 10); //the type of customer's object is int. listbox.anyobj(0, 0, 20); +5. listbox is a widget_object, with template parameters drawerbase::listbox::trigger and drawerbase::listbox::scheme +amon others. +That means that listbox have a member trigger_ constructed first and accecible with get_drawer_trigger() and +a member (unique pointer to) scheme_ accesible with scheme_type& scheme() created in the constructor +with API::dev::make_scheme() which call API::detail::make_scheme(::nana::detail::scheme_factory()) +which call restrict::bedrock.make_scheme(static_cast<::nana::detail::scheme_factory_base&&>(factory)); +which call pi_data_->scheme.create(std::move(factory)); +which call factory.create(scheme_template(std::move(factory))); +which call (new Scheme(static_cast(other))); +and which in create is setted with: API::dev::set_scheme(handle_, scheme_.get()); which save the scheme pointer in +the nana::detail::basic_window member pointer scheme \todo doc: actualize this example listbox.at(0)... \see nana::drawerbase::listbox::cat_proxy \see nana::drawerbase::listbox::item_proxy @@ -569,88 +1209,190 @@ By \a clicking on one header the list get \a reordered, first up, and then down public concepts::any_objective { public: + /// An unsigned integral type using size_type = drawerbase::listbox::size_type; + + /// The representation of a category/item using index_pair = drawerbase::listbox::index_pair; + + /// A index_pair package + using index_pairs = drawerbase::listbox::index_pairs; + + /// Iterator to access category using cat_proxy = drawerbase::listbox::cat_proxy; + + /// Iterator to access item using item_proxy = drawerbase::listbox::item_proxy; - using selection = drawerbase::listbox::selection; /// + cat_proxy assoc(Key&& key) + { + using key_type = typename ::nana::detail::type_escape::type>::type; + + auto p = std::make_shared>>(std::forward(key)); + return cat_proxy(&_m_ess(), _m_assoc(p, true)); + } + + /// Returns a proxy to the category of the key or create a new one in the right order + /** + * @param key The key of category to find + * @return A category proxy + */ + template + cat_proxy assoc_at(Key&& key) + { + using key_type = typename ::nana::detail::type_escape::type>::type; + + auto p = std::make_shared>>(std::forward(key)); + + auto categ = _m_assoc(p, false); + if (nullptr == categ) + throw std::out_of_range("listbox: invalid key."); + + return cat_proxy(&_m_ess(), categ); + } + + /// Removes a category which is associated with the specified key + /** + * @param key The key of category to remove + */ + template + void assoc_erase(Key&& key) + { + using key_type = typename ::nana::detail::type_escape::type>::type; + + ::nana::key> wrap(key); + _m_erase_key(&wrap); + } + + bool assoc_ordered(bool); + + + void auto_draw(bool); ///< Set state: Redraw automatically after an operation + + template + void avoid_drawing(Function fn) + { + this->auto_draw(false); + try + { + fn(); + } + catch (...) + { + this->auto_draw(true); + throw; + } + this->auto_draw(true); + } /// Scrolls the view to the first or last item of a specified category void scroll(bool to_bottom, size_type cat_pos = ::nana::npos); void scroll(bool to_bottom, const index_pair& pos); - /// Appends a new column with a header text and the specified width at the end, and return it position + /// Appends a new column with a header text and the specified width at the end, and return it position size_type append_header(std::string text_utf8, unsigned width = 120); size_type append_header(std::wstring text, unsigned width = 120); - listbox& header_width(size_type position, unsigned pixels); - unsigned header_width(size_type position) const; - unsigned auto_width(size_type position, unsigned max=3000); + /// Access a column at specified position + /** + * @param pos Position of column + * @return Reference to the requested column + * @except std::out_of_range if !(pos < columns()) + */ + column_interface & column_at(size_type pos); - cat_proxy append(std::string); ///< Appends a new category to the end - cat_proxy append(std::wstring); ///< Appends a new category to the end + /// Access a column at specified position + /** + * @param pos Position of column + * @return Constant reference to the requested column + * @except std::out_of_range if !(pos < columns()) + */ + const column_interface & column_at(size_type pos) const; + + /// Returns the number of columns + size_type column_size() const; + + cat_proxy append(std::string); ///< Appends a new category to the end + cat_proxy append(std::wstring); ///< Appends a new category to the end void append(std::initializer_list); ///< Appends categories to the end void append(std::initializer_list); ///< Appends categories to the end cat_proxy insert(cat_proxy, ::std::string); cat_proxy insert(cat_proxy, ::std::wstring); - cat_proxy at(size_type pos) const; - /// add categories in order when use a key? - listbox& ordered_categories(bool); + /// Inserts an item before a specified position + /** + * @param abs_pos The absolute position before which an item will be inserted. + * @param text Text of the first column, in UTF-8 encoded. + */ + void insert_item(const index_pair& abs_pos, ::std::string text); - /// return a proxy to tha cat with the key or create a new one in the right order - template - cat_proxy operator[](const Key & ck) - { - using catkey = typename ::nana::detail::type_escape::type; - std::shared_ptr p(new nana::key>(ck), [](nana::detail::key_interface* p) - { - delete p; - }); - - return cat_proxy(&_m_ess(), _m_at_key(p)); - } - - template - cat_proxy operator[](Key && ck) - { - using catkey = typename ::nana::detail::type_escape::type; - std::shared_ptr p(new nana::key>(std::move(ck)), [](nana::detail::key_interface* p) - { - delete p; - }); - - return cat_proxy(&_m_ess(), _m_at_key(p)); - } - - /// Returns an item by the specified absolute position - item_proxy at(const index_pair &abs_pos) const; + /// Inserts an item before a specified position + /** + * @param abs_pos The absolute position before which an item will be inserted. + * @param text Text of the first column. + */ + void insert_item(const index_pair& abs_pos, const ::std::wstring& text); /// Returns an index of item which contains the specified point. - index_pair at(const point & pos) const; + index_pair cast(const point & pos) const; /// Returns the column which contains the specified point. - columns_indexs column_from_pos(const point & pos); - - - void insert(const index_pair&, ::std::string); /// - void erase_key(const Key& kv) - { - typedef typename nana::detail::type_escape::type key_t; - nana::key > key(kv); - _m_erase_key(&key); - } - - template - void erase_key(Key&& kv) - { - typedef typename nana::detail::type_escape::type key_t; - nana::key > key(std::move(kv)); - _m_erase_key(&key); - } - bool sortable() const; void sortable(bool enable); ///Sets a strict weak ordering comparer for a column - void set_sort_compare(size_type col, std::function strick_ordering); + void set_sort_compare( size_type col, + 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); + void sort_col(size_type col, bool reverse = false); size_type sort_col() const; - /// potencially ivalidate any existing reference from display position to absolute item, that is: after sort() display offset point to different items - void unsort(); + /// potencially ivalidate any existing reference from display position to absolute item, that is: after sort() display offset point to different items + void unsort(); bool freeze_sort(bool freeze); - selection selected() const; ///); + drawerbase::listbox::category_t* _m_assoc(std::shared_ptr, bool create_if_not_exists); void _m_erase_key(nana::detail::key_interface*); }; }//end namespace nana + +#include #endif diff --git a/include/nana/gui/widgets/menu.hpp b/include/nana/gui/widgets/menu.hpp index 335796d4..02372079 100644 --- a/include/nana/gui/widgets/menu.hpp +++ b/include/nana/gui/widgets/menu.hpp @@ -17,6 +17,8 @@ #include #include +#include + namespace nana { namespace drawerbase @@ -198,4 +200,6 @@ namespace nana detail::popuper menu_popuper(menu&, mouse = mouse::right_button); detail::popuper menu_popuper(menu&, window owner, const point&, mouse = mouse::right_button); }//end namespace nana +#include + #endif diff --git a/include/nana/gui/widgets/menubar.hpp b/include/nana/gui/widgets/menubar.hpp index 365d9b1c..3c799936 100644 --- a/include/nana/gui/widgets/menubar.hpp +++ b/include/nana/gui/widgets/menubar.hpp @@ -14,6 +14,7 @@ #define NANA_GUI_WIDGETS_MENUBAR_HPP #include "widget.hpp" #include "menu.hpp" +#include namespace nana { @@ -117,4 +118,6 @@ namespace nana ::nana::event_handle evt_resized_{nullptr}; };//end class menubar }//end namespace nana +#include + #endif diff --git a/include/nana/gui/widgets/picture.hpp b/include/nana/gui/widgets/picture.hpp index bd40f0e0..fc8e9c63 100644 --- a/include/nana/gui/widgets/picture.hpp +++ b/include/nana/gui/widgets/picture.hpp @@ -13,6 +13,8 @@ */ #ifndef NANA_GUI_WIDGET_PICTURE_HPP #define NANA_GUI_WIDGET_PICTURE_HPP +#include + #include "widget.hpp" namespace nana @@ -67,4 +69,6 @@ namespace nana bool transparent() const; }; }//end namespace nana +#include + #endif diff --git a/include/nana/gui/widgets/scroll.hpp b/include/nana/gui/widgets/scroll.hpp index 96d1b023..dbbb6452 100644 --- a/include/nana/gui/widgets/scroll.hpp +++ b/include/nana/gui/widgets/scroll.hpp @@ -15,6 +15,7 @@ #include "widget.hpp" #include +#include namespace nana { @@ -205,14 +206,14 @@ namespace nana void resized(graph_reference graph, const ::nana::arg_resized&) override { drawer_.draw(graph, metrics_.what); - API::lazy_refresh(); + 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); - API::lazy_refresh(); + API::dev::lazy_refresh(); } void mouse_move(graph_reference graph, const ::nana::arg_mouse& arg) override @@ -238,7 +239,7 @@ namespace nana if (redraw) { drawer_.draw(graph, metrics_.what); - API::lazy_refresh(); + API::dev::lazy_refresh(); } } @@ -262,7 +263,7 @@ namespace nana timer_.start(); break; case buttons::scroll: - API::capture_window(widget_->handle(), true); + widget_->set_capture(true); metrics_.scroll_mouse_offset = (Vertical ? arg.pos.y : arg.pos.x) - metrics_.scroll_pos; break; case buttons::forward: @@ -278,7 +279,7 @@ namespace nana break; } drawer_.draw(graph, metrics_.what); - API::lazy_refresh(); + API::dev::lazy_refresh(); } } @@ -286,12 +287,12 @@ namespace nana { timer_.stop(); - API::capture_window(widget_->handle(), false); + widget_->release_capture(); metrics_.pressed = false; metrics_.what = drawer_.what(graph, arg.pos); drawer_.draw(graph, metrics_.what); - API::lazy_refresh(); + API::dev::lazy_refresh(); } void mouse_leave(graph_reference graph, const arg_mouse&) override @@ -300,7 +301,7 @@ namespace nana metrics_.what = buttons::none; drawer_.draw(graph, buttons::none); - API::lazy_refresh(); + API::dev::lazy_refresh(); } void mouse_wheel(graph_reference graph, const arg_wheel& arg) override @@ -308,13 +309,13 @@ namespace nana if (make_step(arg.upwards == false, 3)) { drawer_.draw(graph, metrics_.what); - API::lazy_refresh(); + API::dev::lazy_refresh(); } } private: void _m_emit_value_changed() { - widget_->events().value_changed.emit({ widget_->handle() }); + widget_->events().value_changed.emit({ widget_->handle() }, widget_->handle()); } void _m_tick() @@ -502,4 +503,5 @@ namespace nana } };//end class scroll }//end namespace nana +#include #endif diff --git a/include/nana/gui/widgets/skeletons/text_editor.hpp b/include/nana/gui/widgets/skeletons/text_editor.hpp index 3189cd18..d2a10a35 100644 --- a/include/nana/gui/widgets/skeletons/text_editor.hpp +++ b/include/nana/gui/widgets/skeletons/text_editor.hpp @@ -13,6 +13,8 @@ #ifndef NANA_GUI_SKELETONS_TEXT_EDITOR_HPP #define NANA_GUI_SKELETONS_TEXT_EDITOR_HPP +#include + #include "textbase.hpp" #include "text_editor_part.hpp" #include @@ -40,6 +42,12 @@ namespace nana{ namespace widgets using command = EnumCommand; using container = std::deque < std::unique_ptr> >; + void clear() + { + commands_.clear(); + pos_ = 0; + } + void max_steps(std::size_t maxs) { max_steps_ = maxs; @@ -56,7 +64,7 @@ namespace nana{ namespace widgets { enabled_ = enb; if (!enb) - commands_.clear(); + clear(); } bool enabled() const @@ -130,6 +138,7 @@ namespace nana{ namespace widgets struct keywords; class keyword_parser; + class helper_pencil; public: using char_type = wchar_t; using size_type = textbase::size_type; @@ -202,7 +211,7 @@ namespace nana{ namespace widgets unsigned screen_lines() const; bool getline(std::size_t pos, ::std::wstring&) const; - void text(std::wstring); + void text(std::wstring, bool end_caret); std::wstring text() const; /// Sets caret position through text coordinate. @@ -218,7 +227,7 @@ namespace nana{ namespace widgets void set_end_caret(); bool hit_text_area(const point&) const; - bool hit_select_area(nana::upoint pos) const; + bool hit_select_area(nana::upoint pos, bool ignore_when_select_all) const; bool move_select(); bool mask(wchar_t); @@ -229,6 +238,9 @@ namespace nana{ namespace widgets /// Returns text position of each line that currently displays on screen const std::vector& text_position() const; + + void focus_behavior(text_focus_behavior); + void select_behavior(bool move_to_end); public: void draw_corner(); void render(bool focused); @@ -249,17 +261,20 @@ namespace nana{ namespace widgets const upoint& caret() const; point caret_screen_pos() const; bool scroll(bool upwards, bool vertical); - bool mouse_enter(bool); + + bool focus_changed(const arg_focus&); + bool mouse_enter(bool entering); bool mouse_move(bool left_button, const point& screen_pos); bool mouse_pressed(const arg_mouse& arg); skeletons::textbase& textbase(); const skeletons::textbase& textbase() const; private: + void _m_pre_calc_lines(std::size_t line_off, std::size_t lines); + bool _m_accepts(char_type) const; ::nana::color _m_bgcolor() const; bool _m_scroll_text(bool vertical); - void _m_on_scroll(const arg_mouse&); void _m_scrollbar(); ::nana::size _m_text_area() const; void _m_get_scrollbar_size(); @@ -267,7 +282,7 @@ namespace nana{ namespace widgets ::nana::upoint _m_put(::std::wstring); ::nana::upoint _m_erase_select(); - bool _m_make_select_string(::std::wstring&) const; + ::std::wstring _m_make_select_string() const; static bool _m_resolve_text(const ::std::wstring&, std::vector> & lines); bool _m_cancel_select(int align); @@ -295,14 +310,16 @@ namespace nana{ namespace widgets void _m_offset_y(int y); - unsigned _m_char_by_pixels(const wchar_t*, std::size_t len, unsigned* pxbuf, int str_px, int pixels, bool is_rtl); - unsigned _m_pixels_by_char(const ::std::wstring&, std::size_t pos) const; + unsigned _m_char_by_pixels(const unicode_bidi::entity&, unsigned pos); + + unsigned _m_pixels_by_char(const ::std::wstring&, ::std::size_t pos) const; void _handle_move_key(const arg_keyboard& arg); private: std::unique_ptr behavior_; undoable undo_; nana::window window_; + std::unique_ptr caret_; graph_reference graph_; const text_editor_scheme* scheme_; event_interface * event_handler_{ nullptr }; @@ -353,10 +370,12 @@ namespace nana{ namespace widgets struct selection { - enum mode_selection_t{mode_no_selected, mode_mouse_selected, mode_method_selected}; + enum class mode{ no_selected, mouse_selected, method_selected, move_selected }; - mode_selection_t mode_selection; - bool dragged; + text_focus_behavior behavior; + bool move_to_end; + mode mode_selection; + bool ignore_press; nana::upoint a, b; }select_; @@ -372,5 +391,7 @@ namespace nana{ namespace widgets }//end namespace widgets }//end namespace nana +#include + #endif diff --git a/include/nana/gui/widgets/skeletons/text_editor_part.hpp b/include/nana/gui/widgets/skeletons/text_editor_part.hpp index be4ec920..0b5eb266 100644 --- a/include/nana/gui/widgets/skeletons/text_editor_part.hpp +++ b/include/nana/gui/widgets/skeletons/text_editor_part.hpp @@ -2,7 +2,7 @@ #ifndef NANA_WIDGETS_SKELETONS_TEXT_EDITOR_SCHEME_HPP #define NANA_WIDGETS_SKELETONS_TEXT_EDITOR_SCHEME_HPP -#include "../../detail/widget_colors.hpp" +#include "../../detail/widget_geometrics.hpp" #include namespace nana @@ -11,14 +11,25 @@ namespace nana { namespace skeletons { + enum class text_focus_behavior + { + none, + select, + select_if_tabstop, + select_if_click, + select_if_tabstop_or_click + }; + //forward declaration class text_editor; struct text_editor_scheme - : public ::nana::widget_colors + : public ::nana::widget_geometrics { color_proxy selection{static_cast(0x3399FF)}; color_proxy selection_text{colors::white}; + + parameters::mouse_wheel mouse_wheel; ///< The number of lines/characters to scroll when the vertical/horizontal mouse wheel is moved. }; class text_editor_event_interface diff --git a/include/nana/gui/widgets/skeletons/text_token_stream.hpp b/include/nana/gui/widgets/skeletons/text_token_stream.hpp index 7fbd3801..3807ac16 100644 --- a/include/nana/gui/widgets/skeletons/text_token_stream.hpp +++ b/include/nana/gui/widgets/skeletons/text_token_stream.hpp @@ -15,13 +15,14 @@ #include -#include #include #include #include #include #include +#include + namespace nana{ namespace widgets{ namespace skeletons { //The tokens are defined for representing a text, the tokens are divided @@ -86,26 +87,12 @@ namespace nana{ namespace widgets{ namespace skeletons std::pair binary_number() const { - std::stringstream ss; - ss<(nana::charset(binary_.first))<<' '<(nana::charset(binary_.second)); - - std::pair r; - ss>>r.first>>r.second; - return r; + return{ std::stoul(binary_.first), std::stoul(binary_.second) }; } int number() const { - std::stringstream ss; - ss<(nana::charset(idstr_)); - - //It's a hex number. - if(idstr_.size() > 2 && idstr_[0] == '0' && (idstr_[1] == 'x' || idstr_[1] == 'X')) - ss>>std::hex; - - int n; - ss>>n; - return n; + return std::stoi(idstr_, nullptr, 0); } private: static bool _m_unicode_word_breakable(wchar_t ch) @@ -192,13 +179,24 @@ namespace nana{ namespace widgets{ namespace skeletons return token::tag_begin; } + //Escape - if(ch == '\\') + if(this->format_enabled_ && (ch == '\\')) { if(iptr_ + 1 < endptr_) { ch = *(iptr_ + 1); - iptr_ += 2; + + if ('<' == ch || '>' == ch) //two characters need to be escaped. + { + iptr_ += 2; + } + else + { + //ignore escape + ch = '\\'; + iptr_++; + } } else { @@ -206,8 +204,8 @@ namespace nana{ namespace widgets{ namespace skeletons return token::eof; } } - - ++iptr_; + else + ++iptr_; idstr_.clear(); idstr_.append(1, ch); @@ -275,6 +273,8 @@ namespace nana{ namespace widgets{ namespace skeletons return token::eof; } + + if(('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || '_' == ch) { --iptr_; @@ -665,8 +665,7 @@ namespace nana{ namespace widgets{ namespace skeletons case token::eof: return; default: - int * debug = 0; //for debug. - *debug = 0; + throw std::runtime_error("invalid token"); } } } @@ -941,4 +940,5 @@ namespace nana{ namespace widgets{ namespace skeletons }//end namespace skeletons }//end namespace widgets }//end namepsace nana +#include #endif //NANA_GUI_WIDGETS_SKELETONS_TEXT_TOKEN_STREAM diff --git a/include/nana/gui/widgets/skeletons/textbase.hpp b/include/nana/gui/widgets/skeletons/textbase.hpp index 99cc5799..55828351 100644 --- a/include/nana/gui/widgets/skeletons/textbase.hpp +++ b/include/nana/gui/widgets/skeletons/textbase.hpp @@ -13,6 +13,7 @@ #ifndef NANA_GUI_WIDGET_DETAIL_TEXTBASE_HPP #define NANA_GUI_WIDGET_DETAIL_TEXTBASE_HPP +#include #include #include @@ -134,7 +135,7 @@ namespace skeletons while(ifs.good()) { std::getline(ifs, str_mbs); - text_cont_.emplace_back(nana::charset(str_mbs)); + 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(); @@ -217,7 +218,7 @@ namespace skeletons byte_order_translate_4bytes(str); } - text_cont_.emplace_back(nana::charset(str, encoding)); + text_cont_.emplace_back(static_cast(nana::charset{ str, encoding })); attr_max_.size = text_cont_.back().size(); attr_max_.line = 0; @@ -235,7 +236,7 @@ namespace skeletons byte_order_translate_4bytes(str); } - text_cont_.emplace_back(nana::charset(str, encoding)); + 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(); @@ -405,7 +406,7 @@ namespace skeletons void erase_all() { - std::deque().swap(text_cont_); + text_cont_.clear(); attr_max_.reset(); text_cont_.emplace_back(); //text_cont_ must not be empty @@ -536,4 +537,6 @@ namespace skeletons }//end namespace detail }//end namespace widgets }//end namespace nana +#include + #endif diff --git a/include/nana/gui/widgets/slider.hpp b/include/nana/gui/widgets/slider.hpp index c5372034..9adb512c 100644 --- a/include/nana/gui/widgets/slider.hpp +++ b/include/nana/gui/widgets/slider.hpp @@ -11,6 +11,9 @@ */ #ifndef NANA_GUI_WIDGETS_SLIDER_HPP #define NANA_GUI_WIDGETS_SLIDER_HPP + +#include + #include "widget.hpp" #include @@ -30,6 +33,23 @@ namespace nana { namespace slider { + + struct scheme_impl + : public widget_geometrics + { + /// Colors + color_proxy color_adorn { static_cast(0x3da3ce) }; + color_proxy color_bar { static_cast(0x878787) }; + color_proxy color_slider { static_cast(0x606060) }; + color_proxy color_slider_highlighted{ static_cast(0x2d93be) }; + color_proxy color_vernier { colors::red }; + color_proxy color_vernier_text { colors::white }; + + /// Geometrical parameters + unsigned vernier_text_margin{ 8 }; + + }; + struct slider_events : public general_events { @@ -41,65 +61,66 @@ namespace nana bilateral, forward, backward }; - class provider + + class renderer_interface { public: - virtual ~provider() = default; - virtual std::string adorn_trace(unsigned vmax, unsigned vadorn) const = 0; - }; + using graph_reference = ::nana::paint::graphics&; + using scheme = scheme_impl; - class renderer - { - public: - typedef ::nana::paint::graphics & graph_reference; - - struct bar_t + struct data_bar { - bool horizontal; - nana::rectangle r; //the rectangle of bar. - unsigned border_size; //border_size of bar. + bool vert; ///< Indicates whether the slider is vertical. + ::nana::rectangle area; ///< Position and size of bar. + unsigned border_weight; ///< The border weight in pixels. }; - struct slider_t + struct data_slider { - bool horizontal; - int pos; - unsigned border; - unsigned scale; + bool vert; ///< Indicates whether the slider is vertical. + double pos; + unsigned border_weight; + unsigned weight; }; - struct adorn_t + struct data_adorn { - bool horizontal; - nana::point bound; + bool vert; ///< Indicates whether the slider is vertical. + ::nana::point bound; int fixedpos; unsigned block; - unsigned vcur_scale; //pixels of vcur scale. + unsigned vcur_scale; }; - virtual ~renderer() = default; + struct data_vernier + { + bool vert; ///< Indicates whether the slider is vertical. + int position; + int end_position; + unsigned knob_weight; - virtual void background(window, graph_reference, bool isglass) = 0; - virtual void adorn(window, graph_reference, const adorn_t&) = 0; - virtual void adorn_textbox(window, graph_reference, const ::std::string&, const nana::rectangle&) = 0; - virtual void bar(window, graph_reference, const bar_t&) = 0; - virtual void slider(window, graph_reference, const slider_t&) = 0; + std::string text; + }; + + virtual ~renderer_interface() = default; + + virtual void background(window, graph_reference, bool transparent, const scheme&) = 0; + virtual void adorn(window, graph_reference, const data_adorn&, const scheme&) = 0; + virtual void vernier(window, graph_reference, const data_vernier&, const scheme&) = 0; + virtual void bar(window, graph_reference, const data_bar&, const scheme&) = 0; + virtual void slider(window, graph_reference, mouse_action, const data_slider&, const scheme&) = 0; }; - class controller; - class trigger : public drawer_trigger { + class model; public: - typedef controller controller_t; - trigger(); ~trigger(); - controller_t* ctrl() const; + model* get_model() const; private: void attached(widget_reference, graph_reference) override; - void detached() override; void refresh(graph_reference) override; void mouse_down(graph_reference, const arg_mouse&) override; void mouse_up(graph_reference, const arg_mouse&) override; @@ -107,18 +128,19 @@ namespace nana void mouse_leave(graph_reference, const arg_mouse&) override; void resized(graph_reference, const arg_resized&) override; private: - controller_t * impl_; + model * model_ptr_; }; }//end namespace slider }//end namespace drawerbase - /// A slider widget wich the user can drag for tracking + + + /// A slider widget wich the user can drag for tracking \todo add scheme ? class slider - : public widget_object + : public widget_object { public: - typedef drawerbase::slider::renderer renderer; ///< The interface for user-defined renderer. - typedef drawerbase::slider::provider provider; ///< The interface for user-defined provider. - typedef drawerbase::slider::seekdir seekdir; ///< Defines the slider seek direction. + using renderer_interface = drawerbase::slider::renderer_interface; ///< The interface for customized renderer. + using seekdir = drawerbase::slider::seekdir; ///< Defines the slider seek direction. slider(); slider(window, bool visible); @@ -127,19 +149,21 @@ namespace nana void seek(seekdir); ///< Define the direction that user can seek by using mouse. void vertical(bool); bool vertical() const; - void vmax(unsigned); - unsigned vmax() const; + void maximum(unsigned); + unsigned maximum() const; void value(unsigned); unsigned value() const; unsigned move_step(bool forward); ///< Increase or decrease the value of slider. unsigned adorn() const; - pat::cloneable& ext_renderer(); ///< Refers to the current renderer that slider is using. - void ext_renderer(const pat::cloneable&); ///< Set the current renderer. - void ext_provider(const pat::cloneable&); + const pat::cloneable& renderer(); ///< Refers to the current renderer that slider is using. + void renderer(const pat::cloneable&); ///< Set the current renderer. + + void vernier(std::function provider); void transparent(bool); bool transparent() const; }; }//end namespace nana +#include #endif diff --git a/include/nana/gui/widgets/spinbox.hpp b/include/nana/gui/widgets/spinbox.hpp index e3d28505..e090426a 100644 --- a/include/nana/gui/widgets/spinbox.hpp +++ b/include/nana/gui/widgets/spinbox.hpp @@ -12,6 +12,8 @@ #ifndef NANA_GUI_WIDGET_SPINBOX_HPP #define NANA_GUI_WIDGET_SPINBOX_HPP + +#include #include "widget.hpp" #include "skeletons/text_editor_part.hpp" @@ -68,7 +70,7 @@ namespace nana private: implementation * const impl_; }; - }; + } }//end namespace drawerbase /// Spinbox Widget @@ -110,5 +112,5 @@ namespace nana void _m_caption(native_string_type&&); }; //end class spinbox }//end namespace nana - +#include #endif //NANA_GUI_WIDGET_SPINBOX_HPP diff --git a/include/nana/gui/widgets/tabbar.hpp b/include/nana/gui/widgets/tabbar.hpp index b7a88789..d8979911 100644 --- a/include/nana/gui/widgets/tabbar.hpp +++ b/include/nana/gui/widgets/tabbar.hpp @@ -13,6 +13,8 @@ */ #ifndef NANA_GUI_WIDGET_TABBAR_HPP #define NANA_GUI_WIDGET_TABBAR_HPP +#include + #include "widget.hpp" #include #include @@ -111,14 +113,14 @@ namespace nana if(pos != npos) { drawer_trigger_.at_no_bound_check(pos) = T(); - tabbar_.events().added.emit(arg_tabbar({ tabbar_, tabbar_[pos] })); + tabbar_.events().added.emit(arg_tabbar({ tabbar_, tabbar_[pos] }), tabbar_); } } void activated(std::size_t pos) override { if(pos != npos) - tabbar_.events().activated.emit(arg_tabbar({ tabbar_, tabbar_[pos]})); + tabbar_.events().activated.emit(arg_tabbar({ tabbar_, tabbar_[pos]}), tabbar_); } bool removed(std::size_t pos, bool & close_attach) override @@ -126,7 +128,7 @@ namespace nana if (pos != npos) { ::nana::arg_tabbar_removed arg(tabbar_, tabbar_[pos]); - tabbar_.events().removed.emit(arg); + tabbar_.events().removed.emit(arg, tabbar_); close_attach = arg.close_attach_window; return arg.remove; } @@ -410,5 +412,6 @@ namespace nana void erase(std::size_t pos, bool close_attached = true); }; } +#include #endif diff --git a/include/nana/gui/widgets/textbox.hpp b/include/nana/gui/widgets/textbox.hpp index 69fda830..12008c37 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-2015 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -11,6 +11,8 @@ */ #ifndef NANA_GUI_WIDGET_TEXTBOX_HPP #define NANA_GUI_WIDGET_TEXTBOX_HPP +#include + #include #include "skeletons/textbase_export_interface.hpp" #include "skeletons/text_editor_part.hpp" @@ -98,6 +100,8 @@ namespace nana :public widget_object { public: + using text_focus_behavior = widgets::skeletons::text_focus_behavior; + using text_positions = std::vector; /// The default constructor without creating the widget. textbox(); @@ -134,8 +138,14 @@ namespace nana /// @param generator generates text for identing a line. If it is empty, textbox indents the line according to last line. textbox& indention(bool, std::function generator = {}); - //A workaround for reset, explicit default constructor syntax, because VC2013 incorrectly treats {} as {0}. - textbox& reset(const std::string& = std::string()); ///< discard the old text and set a new text + /// Discards the old text and set a new text. It also clears the filename/edited flags and undo command. + /// A workaround for reset, explicit default constructor syntax, because VC2013 incorrectly treats {} as {0}. + /* + * @param text A new text replaces the old text. + * @param end_caret Indicates whether to position the caret to the end of text. + * @return a reference of *this. + */ + 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; @@ -162,15 +172,18 @@ namespace nana /// Appends an string. If `at_caret` is `true`, the string is inserted at the position of caret, otherwise, it is appended at end of the textbox. textbox& append(const std::string& text, bool at_caret); - /// Determine wheter the text is line wrapped. + /// Determines whether the text is line wrapped. bool line_wrapped() const; textbox& line_wrapped(bool); - /// Determine whether the text is multi-line enabled. + /// Determines whether the text is multi-line enabled. bool multi_lines() const; textbox& multi_lines(bool); + + /// Determines whether the textbox accepts user input bool editable() const; textbox& editable(bool); + void set_accept(std::function); textbox& tip_string(::std::string); @@ -207,6 +220,14 @@ namespace nana /// Returns the height of line in pixels unsigned line_pixels() const; + + /// Sets the behavior when textbox gets focus. + void focus_behavior(text_focus_behavior); + + /// Sets the caret move behavior when the content of textbox is selected. + /// E.g. Whether caret moves to left of selected content or moves to left of last position when left arrow key is pressed. + /// @param move_to_end determines whether to move caret to left of selected_content or to left of last position. + void select_behavior(bool move_to_end); protected: //Overrides widget's virtual functions native_string_type _m_caption() const throw() override; @@ -214,4 +235,6 @@ namespace nana void _m_typeface(const paint::font&) override; }; }//end namespace nana +#include + #endif diff --git a/include/nana/gui/widgets/toolbar.hpp b/include/nana/gui/widgets/toolbar.hpp index 8a4d3a40..24f27b32 100644 --- a/include/nana/gui/widgets/toolbar.hpp +++ b/include/nana/gui/widgets/toolbar.hpp @@ -12,6 +12,7 @@ #ifndef NANA_GUI_WIDGET_TOOLBAR_HPP #define NANA_GUI_WIDGET_TOOLBAR_HPP +#include #include "widget.hpp" @@ -101,4 +102,6 @@ namespace nana bool detached_; }; }//end namespace nana +#include + #endif diff --git a/include/nana/gui/widgets/treebox.hpp b/include/nana/gui/widgets/treebox.hpp index f0cc4cd1..ae5e03b1 100644 --- a/include/nana/gui/widgets/treebox.hpp +++ b/include/nana/gui/widgets/treebox.hpp @@ -17,6 +17,8 @@ #ifndef NANA_GUI_WIDGETS_TREEBOX_HPP #define NANA_GUI_WIDGETS_TREEBOX_HPP + +#include #include "widget.hpp" #include "detail/compset.hpp" #include "detail/tree_cont.hpp" @@ -70,9 +72,7 @@ namespace nana typedef compset_interface::item_attribute_t item_attribute_t; typedef compset_interface::comp_attribute_t comp_attribute_t; - virtual ~renderer_interface() - {} - + virtual ~renderer_interface() = default; virtual void set_color(const nana::color& bgcolor, const nana::color& fgcolor) = 0; virtual void bground(graph_reference, const compset_interface *) const = 0; @@ -307,14 +307,14 @@ namespace nana { _m_value() = t; return *this; - }; + } template item_proxy & value(T&& t) { _m_value() = std::move(t); return *this; - }; + } // Undocumentated methods for internal use trigger::node_type * _m_node() const; @@ -452,4 +452,6 @@ namespace nana item_proxy selected() const; ///< returns the selected node };//end class treebox }//end namespace nana +#include + #endif diff --git a/include/nana/gui/widgets/widget.hpp b/include/nana/gui/widgets/widget.hpp index 1fdb48fa..5e1ad652 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-2015 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -12,11 +12,11 @@ #ifndef NANA_GUI_WIDGET_HPP #define NANA_GUI_WIDGET_HPP -#include "../basis.hpp" + +#include #include "../programming_interface.hpp" #include #include -#include namespace nana { @@ -78,6 +78,15 @@ namespace nana nana::size size() const; void size(const nana::size&); + + /// Enables the widget to grab the mouse input. + /* + * @param ignore_children Indicates whether to redirect the mouse input to its children if the mouse pointer is over its children. + */ + void set_capture(bool ignore_children); + + /// Disables the widget to grab the mouse input. + void release_capture(); point pos() const; void move(int x, int y); @@ -132,9 +141,25 @@ namespace nana virtual nana::color _m_bgcolor() const; }; + namespace detail + { + class widget_base + : public widget + { + public: + ~widget_base(); + + window handle() const override; + private: + void _m_notify_destroy() override final; + protected: + window handle_{ nullptr }; + }; + } + /// Base class of all the classes defined as a widget window. Defaultly a widget_tag - template - class widget_object: public widget + template + class widget_object: public detail::widget_base { protected: typedef DrawerTrigger drawer_trigger_t; @@ -147,12 +172,6 @@ namespace nana scheme_{ API::dev::make_scheme() } {} - ~widget_object() - { - if(handle_) - API::close_window(handle_); - } - event_type& events() const { return *events_; @@ -179,11 +198,6 @@ namespace nana return (this->empty() == false); } - window handle() const override - { - return handle_; - } - widget_object& borderless(bool enable) { API::widget_borderless(handle_, enable); @@ -214,13 +228,7 @@ namespace nana { return *events_; } - - void _m_notify_destroy() override final - { - handle_ = nullptr; - } private: - window handle_{nullptr}; DrawerTrigger trigger_; std::shared_ptr events_; std::unique_ptr scheme_; @@ -228,7 +236,7 @@ namespace nana /// Base class of all the classes defined as a non-graphics-buffer widget window. The second template parameter DrawerTrigger is always ignored.\see nana::panel template - class widget_object: public widget + class widget_object: public detail::widget_base { protected: typedef DrawerTrigger drawer_trigger_t; @@ -240,12 +248,6 @@ namespace nana : events_{ std::make_shared() }, scheme_{ API::dev::make_scheme() } {} - ~widget_object() - { - if(handle_) - API::close_window(handle_); - } - event_type& events() const { return *events_; @@ -269,12 +271,7 @@ namespace nana } return (this->empty() == false); } - - window handle() const override - { - return handle_; - } - + scheme_type& scheme() const { return *scheme_; @@ -284,13 +281,7 @@ namespace nana { return *events_; } - - void _m_notify_destroy() override final - { - handle_ = nullptr; - } private: - window handle_{nullptr}; std::shared_ptr events_; std::unique_ptr scheme_; };//end class widget_object @@ -298,7 +289,7 @@ namespace nana /// Base class of all the classes defined as a root window. \see nana::form template - class widget_object: public widget + class widget_object: public detail::widget_base { protected: typedef DrawerTrigger drawer_trigger_t; @@ -324,12 +315,6 @@ namespace nana _m_bind_and_attach(); } - ~widget_object() - { - if(handle_) - API::close_window(handle_); - } - event_type& events() const { return *events_; @@ -340,11 +325,6 @@ namespace nana API::activate_window(handle_); } - window handle() const override - { - return handle_; - } - native_window_type native_handle() const { return API::root(handle_); @@ -435,25 +415,20 @@ namespace nana { return *events_; } - - void _m_notify_destroy() override final - { - handle_ = nullptr; - } private: - window handle_; - DrawerTrigger trigger_; - std::shared_ptr events_; - std::unique_ptr scheme_; + DrawerTrigger trigger_; + std::shared_ptr events_; + 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 widget + class widget_object: public detail::widget_base { protected: typedef int drawer_trigger_t; @@ -465,12 +440,6 @@ namespace nana : events_{ std::make_shared() }, scheme_{ API::dev::make_scheme() } {} - ~widget_object() - { - if(handle_) - API::close_window(handle_); - } - event_type& events() const { return *events_; @@ -494,11 +463,6 @@ namespace nana return (this->empty() == false); } - window handle() const override - { - return handle_; - } - scheme_type& scheme() const { return *scheme_; @@ -513,15 +477,12 @@ namespace nana { return *events_; } - - void _m_notify_destroy() override final - { - handle_ = nullptr; - } private: - window handle_{nullptr}; std::shared_ptr events_; std::unique_ptr scheme_; };//end class widget_object -}//end namespace nana +#endif +}//end namespace nana + +#include #endif diff --git a/include/nana/gui/wvl.hpp b/include/nana/gui/wvl.hpp index 1f104bbd..0500a6a0 100644 --- a/include/nana/gui/wvl.hpp +++ b/include/nana/gui/wvl.hpp @@ -1,14 +1,14 @@ -/* +/** * Nana GUI Library Definition * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) * - * @file: nana/gui/wvl.hpp - * @description: + * @file nana/gui/wvl.hpp + * @description * the header file contains the files required for running of Nana.GUI */ @@ -22,6 +22,7 @@ #include "msgbox.hpp" #include "place.hpp" + namespace nana { namespace detail @@ -58,6 +59,25 @@ namespace nana template using form_loader = detail::form_loader; - void exec(); + /// @brief Take control of the GUI and optionaly automaticaly tests it. + /// + /// @detail It transfers to nana the program flow control, which begin pumping messages + /// from the underlying OS, interpreting and sending it with suitable arguments + /// to the nana widgets that registered a response in the corresponding event. + /// It also accept arguments to be used in case of automatic GUI testing. + /// Other Way the arguments are ignored. + void exec( + unsigned wait = 1, ///< for the GUI to be constructed, in seconds + unsigned wait_end = 1, ///< for the GUI to be destructed, in seconds + std::function = {} ///< emit events to mimics user actions and may asert results + ); + + /// send a click message to this widget - useffull in GUI testing + void click(widget& w); + + /// in seconds + void Wait(unsigned wait = 0); + + }//end namespace nana #endif diff --git a/include/nana/key_type.hpp b/include/nana/key_type.hpp index dcfa4f79..9bca1e18 100644 --- a/include/nana/key_type.hpp +++ b/include/nana/key_type.hpp @@ -26,63 +26,63 @@ namespace nana }; //end class key_interface //Use less compare for equal compare [call it equal_by_less()?] - inline bool pred_equal_by_less(const key_interface * left, const key_interface* right) + inline bool pred_equal(const key_interface * left, const key_interface* right) { - return (left->compare(right) == false) && (right->compare(left) == false); + return (left->same_type(right) && (left->compare(right) == false) && (right->compare(left) == false)); } template struct type_escape { - typedef T type; + using type = T; }; template<> struct type_escape { - typedef std::string type; + using type = ::std::string; }; template<> struct type_escape { - typedef std::string type; + using type = ::std::string; }; template struct type_escape { - typedef std::string type; + using type = ::std::string; }; template struct type_escape { - typedef std::string type; + using type = ::std::string; }; template<> struct type_escape { - typedef std::wstring type; + using type = ::std::wstring; }; template<> struct type_escape { - typedef std::wstring type; + using type = ::std::wstring; }; template struct type_escape { - typedef std::wstring type; + using type = ::std::wstring; }; template struct type_escape { - typedef std::wstring type; + using type = ::std::wstring; }; } diff --git a/include/nana/paint/detail/image_impl_interface.hpp b/include/nana/paint/detail/image_impl_interface.hpp index 4a0a1722..3e75d18e 100644 --- a/include/nana/paint/detail/image_impl_interface.hpp +++ b/include/nana/paint/detail/image_impl_interface.hpp @@ -2,7 +2,7 @@ #define NANA_PAINT_DETAIL_IMAGE_IMPL_INTERFACE_HPP #include "../image.hpp" -#include +#include namespace nana{ namespace paint{ @@ -16,7 +16,7 @@ namespace nana{ namespace paint{ public: typedef nana::paint::graphics& graph_reference; virtual ~image_impl_interface() = 0; //The destructor is defined in ../image.cpp - virtual bool open(const nana::experimental::filesystem::path& file) = 0; + virtual bool open(const std::experimental::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/detail/image_processor.hpp b/include/nana/paint/detail/image_processor.hpp index 7cdc1658..4051eb90 100644 --- a/include/nana/paint/detail/image_processor.hpp +++ b/include/nana/paint/detail/image_processor.hpp @@ -330,19 +330,21 @@ namespace detail } const pixel_argb_t * s_end = s_rgb + rest; + auto rest_d_rgb = d_rgb; for(auto i = s_rgb; i != s_end; ++i) { if(i->element.alpha_channel) { if(i->element.alpha_channel != 255) { - d_rgb[3].element.red = unsigned(d_rgb[3].element.red * (255 - i->element.alpha_channel) + i->element.red * i->element.alpha_channel) / 255; - d_rgb[3].element.green = unsigned(d_rgb[3].element.green * (255 - i->element.alpha_channel) + i->element.green * i->element.alpha_channel) / 255; - d_rgb[3].element.blue = unsigned(d_rgb[3].element.blue * (255 - i->element.alpha_channel) + i->element.blue * i->element.alpha_channel) / 255; + rest_d_rgb->element.red = unsigned(rest_d_rgb->element.red * (255 - i->element.alpha_channel) + i->element.red * i->element.alpha_channel) / 255; + rest_d_rgb->element.green = unsigned(rest_d_rgb->element.green * (255 - i->element.alpha_channel) + i->element.green * i->element.alpha_channel) / 255; + rest_d_rgb->element.blue = unsigned(rest_d_rgb->element.blue * (255 - i->element.alpha_channel) + i->element.blue * i->element.alpha_channel) / 255; } else - d_rgb[3] = *i; + *rest_d_rgb = *i; } + ++rest_d_rgb; } d_rgb = pixel_at(d_rgb, d_step_bytes); s_rgb = pixel_at(s_rgb, s_step_bytes); diff --git a/include/nana/paint/graphics.hpp b/include/nana/paint/graphics.hpp index 501d30fc..7102c500 100644 --- a/include/nana/paint/graphics.hpp +++ b/include/nana/paint/graphics.hpp @@ -13,10 +13,11 @@ #ifndef NANA_PAINT_GRAPHICS_HPP #define NANA_PAINT_GRAPHICS_HPP +#include + #include "../basic_types.hpp" #include "../gui/basis.hpp" #include "pixel_buffer.hpp" -#include namespace nana { @@ -111,6 +112,7 @@ namespace nana void bitblt(const ::nana::rectangle& r_dst, const graphics& src); ///< Transfers the color data corresponding to r_dst from the src graphics to this graphics. void bitblt(const ::nana::rectangle& r_dst, const graphics& src, const point& p_src);///< Transfers the color data corresponding to r_dst from the src graphics at point p_src to this graphics. + void blend(const ::nana::rectangle& r, const ::nana::color&, double fade_rate); void blend(const ::nana::rectangle& s_r, graphics& dst, const point& d_pos, double fade_rate) const;///< blends with the dst object. void blur(const ::nana::rectangle& r, std::size_t radius); ///< Blur process. @@ -144,8 +146,6 @@ namespace nana unsigned bidi_string(const nana::point&, const wchar_t *, std::size_t len); unsigned bidi_string(const point& pos, const char*, std::size_t len); - void blend(const ::nana::rectangle& r, const ::nana::color&, double fade_rate); - void set_pixel(int x, int y, const ::nana::color&); void set_pixel(int x, int y); @@ -167,6 +167,7 @@ namespace nana void rectangle(const ::nana::rectangle&, bool solid); void rectangle(const ::nana::rectangle&, bool solid, const color&); void frame_rectangle(const ::nana::rectangle&, const color& left, const color& top, const color& right, const color& bottom); + void frame_rectangle(const ::nana::rectangle&, const color&, unsigned gap); void gradual_rectangle(const ::nana::rectangle&, const color& from, const color& to, bool vertical); void round_rectangle(const ::nana::rectangle&, unsigned radius_x, unsigned radius_y, const color&, bool solid, const color& color_if_solid); diff --git a/include/nana/paint/pixel_buffer.hpp b/include/nana/paint/pixel_buffer.hpp index 49d2fc4e..6b6a4994 100644 --- a/include/nana/paint/pixel_buffer.hpp +++ b/include/nana/paint/pixel_buffer.hpp @@ -75,6 +75,7 @@ namespace nana{ namespace paint void blend(const nana::rectangle& s_r, drawable_type dw_dst, const nana::point& d_pos, double fade_rate) const; void blur(const nana::rectangle& r, std::size_t radius); + pixel_buffer rotate(double angle, const color& extend_color); private: std::shared_ptr storage_; }; diff --git a/include/nana/paint/text_renderer.hpp b/include/nana/paint/text_renderer.hpp index 7234c57c..96fbbfdb 100644 --- a/include/nana/paint/text_renderer.hpp +++ b/include/nana/paint/text_renderer.hpp @@ -9,7 +9,7 @@ namespace nana class text_renderer { public: - typedef graphics & graph_reference; + using graph_reference = graphics &; text_renderer(graph_reference graph, align = align::left); @@ -22,6 +22,35 @@ namespace nana graph_reference graph_; align text_align_; }; + + /// Draw aligned string + class aligner + { + public: + using graph_reference = graphics&; + + /// Constructor + /** + * @param graph Reference to a graphics object + * @param text_align Alignment of text + * @param text_align_if_too_long Alignment of text if the pixels of string is larger than text area + */ + aligner(graph_reference graph, align text_align = align::left); + aligner(graph_reference graph, align text_align, align text_align_if_too_long); + + /// Draws a text with specified text alignment. + /** + * @param text Text to draw + * @param pos Postion where the text to draw + * @param width The width of text area. If the pixels of text is larger than this parameter, it draws ellipsis + */ + void draw(const std::string& text, point pos, unsigned width); + void draw(const std::wstring& text, point pos, unsigned width); + private: + graph_reference graph_; + align text_align_; + align text_align_ex_; + }; } } diff --git a/include/nana/pat/cloneable.hpp b/include/nana/pat/cloneable.hpp index cf3d9d81..c6f5d1c9 100644 --- a/include/nana/pat/cloneable.hpp +++ b/include/nana/pat/cloneable.hpp @@ -13,6 +13,7 @@ #ifndef NANA_PAT_CLONEABLE_HPP #define NANA_PAT_CLONEABLE_HPP +#include #include #include #include @@ -97,9 +98,7 @@ namespace nana{ namespace pat{ typedef int inner_bool::* operator_bool_t; template - struct member_enabled - : public std::enable_if<(!std::is_base_of::type>::value) && std::is_base_of::type>::value, int> - {}; + using member_enabled = std::enable_if<(!std::is_base_of::type>::value) && std::is_base_of::type>::value, int>; public: cloneable() noexcept = default; @@ -207,5 +206,5 @@ namespace nana{ namespace pat{ using mutable_cloneable = cloneable; }//end namespace pat }//end namespace nana - +#include #endif diff --git a/include/nana/pop_ignore_diagnostic b/include/nana/pop_ignore_diagnostic new file mode 100644 index 00000000..23c39a69 --- /dev/null +++ b/include/nana/pop_ignore_diagnostic @@ -0,0 +1,5 @@ + +#if (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 6) +# pragma GCC diagnostic pop +#endif + diff --git a/include/nana/push_ignore_diagnostic b/include/nana/push_ignore_diagnostic new file mode 100644 index 00000000..9ba463ad --- /dev/null +++ b/include/nana/push_ignore_diagnostic @@ -0,0 +1,4 @@ +#if (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 6) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Weffc++" +#endif diff --git a/include/nana/std_mutex.hpp b/include/nana/std_mutex.hpp index b508548e..38c07aaf 100644 --- a/include/nana/std_mutex.hpp +++ b/include/nana/std_mutex.hpp @@ -8,12 +8,12 @@ #include #include #include -#include -#include +//#include +//#include #include #include // http://lxr.free-electrons.com/source/include/uapi/asm-generic/errno.h#L53 -#define EPROTO 71 /* Protocol error */ +//#define EPROTO 71 /* Protocol error */ #include #include #else diff --git a/include/nana/std_thread.hpp b/include/nana/std_thread.hpp index f3cf4bc0..e1df7ef7 100644 --- a/include/nana/std_thread.hpp +++ b/include/nana/std_thread.hpp @@ -5,6 +5,7 @@ #if defined(STD_THREAD_NOT_SUPPORTED) #if defined(NANA_ENABLE_MINGW_STD_THREADS_WITH_MEGANZ) + #include #else #include @@ -13,5 +14,11 @@ namespace std typedef boost::thread thread; } #endif // (NANA_ENABLE_MINGW_STD_THREADS_WITH_MEGANZ) + +#else + +#include + #endif // (STD_THREAD_NOT_SUPPORTED) + #endif // NANA_STD_THREAD_HPP diff --git a/include/nana/system/shared_wrapper.hpp b/include/nana/system/shared_wrapper.hpp index b1737cc2..ab14997e 100644 --- a/include/nana/system/shared_wrapper.hpp +++ b/include/nana/system/shared_wrapper.hpp @@ -30,7 +30,7 @@ namespace system typedef void* module_t; void* symbols(module_t handle, const char* symbol); - }; //end struct shared_helper + } //end namespace shared_helper }//end namespace detail class shared_wrapper diff --git a/include/nana/unicode_bidi.hpp b/include/nana/unicode_bidi.hpp index acd2b057..71a5f882 100644 --- a/include/nana/unicode_bidi.hpp +++ b/include/nana/unicode_bidi.hpp @@ -1,6 +1,8 @@ #ifndef NANA_UNICODE_BIDI_HPP #define NANA_UNICODE_BIDI_HPP #include +#include + namespace nana { @@ -60,7 +62,7 @@ namespace nana void _m_resolve_weak_types(); void _m_resolve_neutral_types(); void _m_resolve_implicit_levels(); - void _m_reordering_resolved_levels(const char_type*, std::vector & reordered); + void _m_reordering_resolved_levels(std::vector & reordered); static bidi_category _m_bidi_category(bidi_char); static bidi_char _m_char_dir(char_type); private: @@ -71,5 +73,6 @@ namespace nana }; } +#include #endif diff --git a/include/nana/verbose_preprocessor.hpp b/include/nana/verbose_preprocessor.hpp index b62267f6..07b306f8 100644 --- a/include/nana/verbose_preprocessor.hpp +++ b/include/nana/verbose_preprocessor.hpp @@ -11,8 +11,8 @@ * * @brief show the values of configuration constants during compilation to facilitate build debugging. * - * Set VERBOSE_PREPROCESSOR to 1 to show the messages or to 0 for a normal build. - * Normally set to 0. Set to 1 only in case you want to debug the build system because it is extremely repetitive and slow. + * Define VERBOSE_PREPROCESSOR to show the messages or undefine for a normal build. + * Normally undefined. Define in case you want to debug the build system. * * @authors Ariel Vina-Rodriguez (qPCR4vir) * @@ -25,25 +25,107 @@ #if defined(VERBOSE_PREPROCESSOR) + #include + #include + + + #define STRING2(x) #x #define STRING(x) STRING2(x) - #pragma message ( "\nVerbose preprocessor =" STRING(VERBOSE_PREPROCESSOR)" , \n STOP_VERBOSE_PREPROCESSOR=" STRING(STOP_VERBOSE_PREPROCESSOR) ) + #define SHOW_VALUE(x) " " #x " = " STRING2(x) - #pragma message ( "\nWindows: \n _WIN32=" STRING(_WIN32) ", \n __WIN32__ =" STRING(__WIN32__) " , \n WIN32=" STRING(WIN32)" , \n NANA_WINDOWS=" STRING(NANA_WINDOWS) ) + #pragma message ( "\n -----> Verbose preprocessor" ) + #pragma message ( SHOW_VALUE(VERBOSE_PREPROCESSOR) ) + #pragma message ( SHOW_VALUE(STOP_VERBOSE_PREPROCESSOR) ) - #pragma message ( "\nUNICODE: \n NANA_UNICODE=" STRING(NANA_UNICODE) ", \n _UNICODE =" STRING(_UNICODE) " , \n UNICODE=" STRING(UNICODE) ) + #pragma message ( "\n -----> OS: \n --Windows: " ) + #pragma message ( SHOW_VALUE(_WIN32) ) + #pragma message ( SHOW_VALUE(__WIN32__) ) + #pragma message ( SHOW_VALUE(WIN32) ) + #pragma message ( SHOW_VALUE(NANA_WINDOWS) ) - #pragma message ( "\nMinGW: \n __MINGW32__=" STRING(__MINGW32__) ", \n __MINGW64__=" STRING(__MINGW64__) " , \n MINGW=" STRING(MINGW) ) + #pragma message ( "\n ---NIX: " ) + #pragma message ( SHOW_VALUE(NANA_LINUX) ) + #pragma message ( SHOW_VALUE(NANA_POSIX) ) + #pragma message ( SHOW_VALUE(NANA_X11) ) + #pragma message ( SHOW_VALUE(APPLE) ) + #pragma message ( SHOW_VALUE(NANA_IGNORE_CONF) ) - #pragma message ( "\nGNU: \n __GNUC__=" STRING(__GNUC__) ", \n __GNUC_MINOR__=" STRING(__GNUC_MINOR__) " , \n __GNUC_PATCHLEVEL__=" STRING(__GNUC_PATCHLEVEL__) ) - #pragma message ( "\nSTD: \nSTD_CODECVT_NOT_SUPPORTED=" STRING(STD_CODECVT_NOT_SUPPORTED) " , \nSTD_THREAD_NOT_SUPPORTED=" STRING(STD_THREAD_NOT_SUPPORTED) ) + #pragma message ( "\n -----> Compilers: \n MinGW: " ) + #pragma message ( SHOW_VALUE(__MINGW32__) ) + #pragma message ( SHOW_VALUE(__MINGW64__) ) + #pragma message ( SHOW_VALUE(MINGW) ) - #pragma message ( "\nSTD: \nUSE_github_com_meganz_mingw_std_threads=" STRING(USE_github_com_meganz_mingw_std_threads) " , \nSTD_THREAD_NOT_SUPPORTED=" STRING(STD_THREAD_NOT_SUPPORTED) ) + #pragma message ( "\n ---MSC: " ) + #pragma message ( SHOW_VALUE(_MSC_VER) ) + #pragma message ( SHOW_VALUE(_MSC_FULL_VER) ) - #pragma message ( "\nClang compiler: \n__clang__=" STRING(__clang__) ", \n__GLIBCPP__=" STRING(__GLIBCPP__) " , \n__GLIBCXX__=" STRING(__GLIBCXX__) ) + #pragma message ( "\n ---GNU: " ) + #pragma message ( SHOW_VALUE(__GNUC__) ) + #pragma message ( SHOW_VALUE(__GNUC_MINOR__) ) + #pragma message ( SHOW_VALUE(__GNUC_PATCHLEVEL__) ) - #pragma message ( "\nMSC: \n_MSC_VER=" STRING(_MSC_VER) ", \n_MSC_FULL_VER=" STRING(_MSC_FULL_VER ) ) + #pragma message ( "\n ---Clang compiler: " ) + #pragma message ( SHOW_VALUE(__clang__) ) + #pragma message ( SHOW_VALUE(__GLIBCPP__) ) + #pragma message ( SHOW_VALUE(__GLIBCXX__) ) + + #pragma message ( "\n -----> STD: " ) + #pragma message ( SHOW_VALUE(STD_CODECVT_NOT_SUPPORTED) ) + #pragma message ( SHOW_VALUE(STD_THREAD_NOT_SUPPORTED) ) + #pragma message ( SHOW_VALUE(STD_NUMERIC_CONVERSIONS_NOT_SUPPORTED) ) + #pragma message ( SHOW_VALUE(STD_TO_STRING_NOT_SUPPORTED) ) + #pragma message ( SHOW_VALUE(STD_TO_WSTRING_NOT_SUPPORTED) ) + #pragma message ( SHOW_VALUE(USE_github_com_meganz_mingw_std_threads) ) + #pragma message ( SHOW_VALUE(NANA_ENABLE_MINGW_STD_THREADS_WITH_MEGANZ) ) + #pragma message ( SHOW_VALUE(STD_THREAD_NOT_SUPPORTED) ) + #pragma message ( SHOW_VALUE(STD_put_time_NOT_SUPPORTED) ) + #pragma message ( SHOW_VALUE(STD_MAKE_UNIQUE_NOT_SUPPORTED) ) + + #pragma message ( SHOW_VALUE(STD_FILESYSTEM_NOT_SUPPORTED) ) + #pragma message ( SHOW_VALUE(BOOST_FILESYSTEM_AVAILABLE) ) + #pragma message ( SHOW_VALUE(BOOST_FILESYSTEM_FORCE) ) + #pragma message ( SHOW_VALUE(STD_FILESYSTEM_FORCE) ) + #pragma message ( SHOW_VALUE(NANA_FILESYSTEM_FORCE) ) + #pragma message ( SHOW_VALUE(CXX_NO_INLINE_NAMESPACE) ) + //#pragma message ( SHOW_VALUE(__has_include) ) + #pragma message ( SHOW_VALUE(__cpp_lib_experimental_filesystem) ) + #pragma message ( SHOW_VALUE(NANA_USING_NANA_FILESYSTEM) ) + #pragma message ( SHOW_VALUE(NANA_USING_STD_FILESYSTEM) ) + #pragma message ( SHOW_VALUE(NANA_USING_BOOST_FILESYSTEM) ) + + #pragma message ( "\n#include " ) + #include + + #pragma message ( SHOW_VALUE(STD_MAKE_UNIQUE_NOT_SUPPORTED) ) + #pragma message ( SHOW_VALUE(STD_FILESYSTEM_NOT_SUPPORTED) ) + #pragma message ( SHOW_VALUE(BOOST_FILESYSTEM_AVAILABLE) ) + #pragma message ( SHOW_VALUE(BOOST_FILESYSTEM_FORCE) ) + #pragma message ( SHOW_VALUE(STD_FILESYSTEM_FORCE) ) + #pragma message ( SHOW_VALUE(NANA_FILESYSTEM_FORCE) ) + #pragma message ( SHOW_VALUE(CXX_NO_INLINE_NAMESPACE) ) + //#pragma message ( SHOW_VALUE(__has_include) ) + #pragma message ( SHOW_VALUE(__cpp_lib_experimental_filesystem) ) + #pragma message ( SHOW_VALUE(NANA_USING_NANA_FILESYSTEM) ) + #pragma message ( SHOW_VALUE(NANA_USING_STD_FILESYSTEM) ) + #pragma message ( SHOW_VALUE(NANA_USING_BOOST_FILESYSTEM) ) + + #pragma message ( SHOW_VALUE(NANA_UNICODE) ) + #pragma message ( SHOW_VALUE(_UNICODE) ) + #pragma message ( SHOW_VALUE(UNICODE) ) + + #pragma message ( "\n -----> Libraries: " ) + #pragma message ( SHOW_VALUE(NANA_ENABLE_AUDIO) ) + #pragma message ( SHOW_VALUE(NANA_ENABLE_PNG) ) + #pragma message ( SHOW_VALUE(USE_LIBPNG_FROM_OS) ) + #pragma message ( SHOW_VALUE(NANA_LIBPNG) ) + #pragma message ( SHOW_VALUE(NANA_ENABLE_JPEG) ) + #pragma message ( SHOW_VALUE(USE_LIBJPEG_FROM_OS) ) + #pragma message ( SHOW_VALUE(NANA_LIBJPEG) ) + + + // #pragma message ( "\n =" STRING() ", \n =" STRING()" , \n =" STRING() ) #if defined(STOP_VERBOSE_PREPROCESSOR) #error ("\nCompilation stopped to avoid annoying messages") @@ -52,5 +134,4 @@ #endif // VERBOSE_PREPROCESSOR - #endif //NANA_VERBOSE_PREPROCESSOR_H diff --git a/source/audio/player.cpp b/source/audio/player.cpp index e86f1798..5ede06a4 100644 --- a/source/audio/player.cpp +++ b/source/audio/player.cpp @@ -1,5 +1,8 @@ + +#include #include + #ifdef NANA_ENABLE_AUDIO #include @@ -67,4 +70,4 @@ namespace nana{ namespace audio }//end namespace audio }//end namespace nana -#endif //NANA_ENABLE_AUDIO \ No newline at end of file +#endif //NANA_ENABLE_AUDIO diff --git a/source/basic_types.cpp b/source/basic_types.cpp index c8bd2c34..6a40ee59 100644 --- a/source/basic_types.cpp +++ b/source/basic_types.cpp @@ -528,124 +528,25 @@ namespace nana return (px_color().value != other.px_color().value); } + color operator+(const color& x, const color& y) + { + double a = x.a_ + y.a_; + auto r = static_cast(x.r_ + y.r_); + auto g = static_cast(x.g_ + y.g_); + auto b = static_cast(x.b_ + y.b_); + + return color{ + r > 255 ? 255 : r, + g > 255 ? 255 : g, + b > 255 ? 255 : b, + a > 1.0 ? 1.0 : a }; + } //end class color - //struct point - point::point():x(0), y(0){} - point::point(int x, int y):x(x), y(y){} - point::point(const rectangle& r) - : x(r.x), y(r.y) - {} - point& point::operator=(const rectangle& r) - { - x = r.x; - y = r.y; - return *this; - } - - bool point::operator==(const point& rhs) const - { - return ((x == rhs.x) && (y == rhs.y)); - } - - bool point::operator!=(const point& rhs) const - { - return ((x != rhs.x) || (y != rhs.y)); - } - - bool point::operator<(const point& rhs) const - { - return ((y < rhs.y) || (y == rhs.y && x < rhs.x)); - } - - bool point::operator<=(const point& rhs) const - { - return ((y < rhs.y) || (y == rhs.y && x <= rhs.x)); - } - - bool point::operator>(const point& rhs) const - { - return ((y > rhs.y) || (y == rhs.y && x > rhs.x)); - } - - bool point::operator>=(const point& rhs) const - { - return ((y > rhs.y) || (y == rhs.y && x >= rhs.x)); - } - - point point::operator-(const point& rhs) const - { - return{x - rhs.x, y - rhs.y}; - } - - point point::operator+(const point& rhs) const - { - return{ x + rhs.x, y + rhs.y }; - } - - point& point::operator-=(const point& rhs) - { - x -= rhs.x; - y -= rhs.y; - return *this; - } - - point& point::operator+=(const point& rhs) - { - x += rhs.x; - y += rhs.y; - return *this; - } - //end struct point - - //struct upoint - upoint::upoint():x(0), y(0){} - upoint::upoint(unsigned x, unsigned y):x(x), y(y){} - - bool upoint::operator==(const upoint& rhs) const - { - return ((x == rhs.x) && (y == rhs.y)); - } - - bool upoint::operator!=(const upoint& rhs) const - { - return ((x != rhs.x) || (y != rhs.y)); - } - - bool upoint::operator<(const upoint& rhs) const - { - return ((y < rhs.y) || (y == rhs.y && x < rhs.x)); - } - - bool upoint::operator<=(const upoint& rhs) const - { - return ((y < rhs.y) || (y == rhs.y && x <= rhs.x)); - } - - bool upoint::operator>(const upoint& rhs) const - { - return ((y > rhs.y) || (y == rhs.y && x > rhs.x)); - } - - bool upoint::operator>=(const upoint& rhs) const - { - return ((y > rhs.y) || (y == rhs.y && x >= rhs.x)); - } - //end struct upoint //struct size size::size():width(0), height(0){} size::size(value_type width, value_type height) : width(width), height(height){} - size::size(const rectangle& r) - : width(r.width), height(r.height) - {} - - size& size::operator=(const rectangle& r) - { - width = r.width; - height = r.height; - return *this; - } bool size::empty() const { @@ -657,6 +558,12 @@ namespace nana return (0 <= pos.x && pos.x < static_cast(width) && 0 <= pos.y && pos.y < static_cast(height)); } + size& size::shift() + { + std::swap(width, height); + return *this; + } + bool size::operator==(const size& rhs) const { return (width == rhs.width) && (height == rhs.height); @@ -700,33 +607,38 @@ namespace nana return (width != rhs.width) || (height != rhs.height) || (x != rhs.x) || (y != rhs.y); } - rectangle & rectangle::operator=(const point& pos) + point rectangle::position() const noexcept { - x = pos.x; - y = pos.y; + return{ x, y }; + } + + rectangle& rectangle::position(const point& p) noexcept + { + x = p.x; + y = p.y; return *this; } - rectangle & rectangle::operator=(const size & sz) + size rectangle::dimension() const noexcept + { + return{width, height}; + } + + rectangle& rectangle::dimension(const size& sz) noexcept { width = sz.width; height = sz.height; return *this; } - rectangle& rectangle::set_pos(const point& pos) - { - x = pos.x; - y = pos.y; - return *this; - } - + /* rectangle& rectangle::set_size(const size& sz) { width = sz.width; height = sz.height; return *this; } + */ rectangle& rectangle::pare_off(int pixels) { @@ -763,6 +675,13 @@ namespace nana { return (0 == width) || (0 == height); } + + rectangle& rectangle::shift() + { + std::swap(x, y); + std::swap(width, height); + return *this; + } //end struct rectangle //class rectangle_rotator diff --git a/source/charset.cpp b/source/charset.cpp index ddf7207f..eb2fcd55 100644 --- a/source/charset.cpp +++ b/source/charset.cpp @@ -1,4 +1,4 @@ -/* +/** * A Character Encoding Set Implementation * Nana C++ Library(http://www.nanapro.org) * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) @@ -7,9 +7,9 @@ * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) * - * @file: nana/charset.cpp - * @brief: A conversion between unicode characters and multi bytes characters - * @contributions: + * @file nana/charset.cpp + * @brief A conversion between unicode characters and multi bytes characters + * @contributions * UTF16 4-byte decoding issue by Renke Yan. * Pr0curo(pr#98) */ @@ -20,6 +20,7 @@ #include #include #include //Added by Pr0curo(pr#98) +#include //GCC 4.7.0 does not implement the and codecvt_utfx classes #ifndef STD_CODECVT_NOT_SUPPORTED @@ -34,6 +35,7 @@ namespace nana { namespace utf { + /// return a pointer to the code unit of the character at pos const char* char_ptr(const char* text, unsigned pos) { auto ustr = reinterpret_cast(text); @@ -48,10 +50,10 @@ namespace nana continue; } - if (uch < 0xC0) + if (uch < 0xC0) // use police ? return nullptr; - if ((uch < 0xE0) && (ustr + 1 < end)) + if ((uch < 0xE0) && (ustr + 1 < end)) //? *(ustr + 1) < 0xE0 ustr += 2; else if (uch < 0xF0 && (ustr + 2 <= end)) ustr += 3; @@ -64,6 +66,7 @@ namespace nana return reinterpret_cast(ustr); } + /// return a pointer to the code unit of the character at pos - reuse ^ ? const char* char_ptr(const std::string& text_utf8, unsigned pos) { auto ustr = reinterpret_cast(text_utf8.c_str()); @@ -94,6 +97,7 @@ namespace nana return reinterpret_cast(ustr); } + /// return a code point (max 16 bits?) and the len in code units of the character at pos wchar_t char_at(const char* text_utf8, unsigned pos, unsigned * len) { if (!text_utf8) @@ -112,10 +116,10 @@ namespace nana if (len) *len = 1; - return *text_utf8; + return *text_utf8; // uch ? } - if (uch < 0xC0) + if (uch < 0xC0) // use police or ?? { if (len) *len = 0; @@ -151,6 +155,7 @@ namespace nana return 0; } + /// return a code point (max 16 bits?) and the len in code units of the character at pos wchar_t char_at(const ::std::string& text_utf8, unsigned pos, unsigned * len) { const char* ptr; @@ -210,22 +215,23 @@ namespace nana } namespace detail - { + { + /// candidate to be more general?? class locale_initializer { public: static void init() { static bool initialized = false; - if(false == initialized) - { - initialized = true; - //Only set the C library locale - std::setlocale(LC_CTYPE, ""); - } + if (initialized) return; + + initialized = true; + //Only set the C library locale + std::setlocale(LC_CTYPE, ""); } }; + /// convert wchar C string from ? ANSI code page CP_ACP (windows) or LC_CTYPE c locale (-nix) into utf8 std::string bool wc2mb(std::string& mbstr, const wchar_t * s) { if(nullptr == s || *s == 0) @@ -258,7 +264,8 @@ namespace nana #endif return true; } - + + /// convert a char C-string from The system default Windows ANSI code page CP_ACP or from LC_CTYPE c locale (-nix) into utf16 std::wstring bool mb2wc(std::wstring& wcstr, const char* s) { if(nullptr == s || *s == 0) @@ -291,6 +298,7 @@ namespace nana return true; } + /// convert a char C string from The system default Windows ANSI code page CP_ACP or LC_CTYPE c locale (-nix) into utf16 std::string bool mb2wc(std::string& wcstr, const char* s) { if(nullptr == s || *s == 0) @@ -304,6 +312,7 @@ namespace nana { wcstr.resize((chars - 1) * sizeof(wchar_t)); ::MultiByteToWideChar(CP_ACP, 0, s, -1, reinterpret_cast(&wcstr[0]), chars - 1); + // ^ the trick ! } #else locale_initializer::init(); @@ -338,17 +347,98 @@ namespace nana virtual std::wstring&& wstr_move() = 0; }; + /// playing with the idea - we need a mechanisme to set a user selected police - Testing an abtract interphase + struct encoding_error_police + { + virtual unsigned long next_code_point(const unsigned char*& current_code_unit, const unsigned char* end) = 0; + virtual ~encoding_error_police() = default; + }; + + /// the current nana default: it is safe - you may want to keep it ! use the other at your risk: mainly for debugging + struct utf8_error_police : public encoding_error_police + { + unsigned long next_code_point(const unsigned char*& current_code_unit, const unsigned char* end) override + { + current_code_unit = end; + return 0; + } + + }; + + /// + struct utf8_error_police_def_char : public encoding_error_police + { + static unsigned long def_error_mark ; + + unsigned long error_mark{ def_error_mark }; + utf8_error_police_def_char() = default; + utf8_error_police_def_char( unsigned long mark): error_mark{mark}{} + unsigned long next_code_point(const unsigned char*& current_code_unit, const unsigned char* end) override + { + if(current_code_unit < end) + ++current_code_unit; + return error_mark; + } + + }; + + unsigned long utf8_error_police_def_char::def_error_mark{ '*' }; + + /// + struct utf8_error_police_throw : public encoding_error_police + { + unsigned long next_code_point(const unsigned char*& current_code_unit, const unsigned char* end) override + { + //utf8_Error::use_throw = true; + utf8_Error(std::string("The text is not encoded in UTF8: ") + + reinterpret_cast( current_code_unit) ).emit();; + current_code_unit = end; + return 0; + } + + }; + + struct utf8_error_police_latin : public encoding_error_police + { + unsigned long next_code_point(const unsigned char*& current_code_unit, const unsigned char* /*end*/) override + { + return *(current_code_unit++) ; + } + }; + + /// buggie? + struct utf8_error_police_system : public encoding_error_police + { + unsigned long next_code_point(const unsigned char*& current_code_unit, const unsigned char* /*end*/) override + { + std::wstring wc; + mb2wc(wc, reinterpret_cast(current_code_unit)); + current_code_unit++; + + return wc[0]; // use utf16char but what endian? + } + }; + + +// auto def_encoding_error_police = std::make_unique(); // the nana default +// auto def_encoding_error_police = std::make_unique(); +// auto def_encoding_error_police = std::make_unique(); +// auto def_encoding_error_police = std::make_unique('X'); + auto def_encoding_error_police = std::make_unique(); + + + #ifndef STD_CODECVT_NOT_SUPPORTED class charset_string : public charset_encoding_interface { public: charset_string(const std::string& s) - : data_(s), is_unicode_(false) + : data_(s) {} charset_string(std::string&& s) - : data_(std::move(s)), is_unicode_(false) + : data_(std::move(s)) {} charset_string(const std::string& s, unicode encoding) @@ -501,9 +591,9 @@ namespace nana } private: std::string data_; - std::wstring wdata_for_move_; - bool is_unicode_; - unicode utf_x_; + std::wstring wdata_for_move_{}; + bool is_unicode_{ false }; + unicode utf_x_{ unicode::utf8 }; }; class charset_wstring @@ -578,37 +668,42 @@ namespace nana std::string data_for_move_; }; #else + + + /// return the first code point and move the pointer to next character, springing to the end by errors unsigned long utf8char(const unsigned char*& p, const unsigned char* end) { if(p != end) { - if(*p < 0x80) + if(*p < 0x80) // ASCII char 0-127 or 0-0x80 { return *(p++); } unsigned ch = *p; unsigned long code; - if(ch < 0xC0) + if(ch < 0xC0) // error? - move to end. Posible ANSI or ISO code-page { - p = end; - return 0; + //return *(p++); // temp: assume equal + //p = end; + //return 0; + return def_encoding_error_police->next_code_point(p, end); } - else if(ch < 0xE0 && (p + 1 <= end)) + else if(ch < 0xE0 && (p + 1 <= end)) // two byte chararcter { code = ((ch & 0x1F) << 6) | (p[1] & 0x3F); p += 2; } - else if(ch < 0xF0 && (p + 2 <= end)) + else if(ch < 0xF0 && (p + 2 <= end)) // 3 byte character { code = ((((ch & 0xF) << 6) | (p[1] & 0x3F)) << 6) | (p[2] & 0x3F); p += 3; } - else if(ch < 0x1F && (p + 3 <= end)) + else if(ch < 0x1F && (p + 3 <= end)) // 4 byte character { code = ((((((ch & 0x7) << 6) | (p[1] & 0x3F)) << 6) | (p[2] & 0x3F)) << 6) | (p[3] & 0x3F); p += 4; } - else + else // error, go to end { p = end; return 0; @@ -950,11 +1045,11 @@ namespace nana { public: charset_string(const std::string& s) - : data_(s), is_unicode_(false) + : data_(s) {} charset_string(std::string&& s) - : data_(std::move(s)), is_unicode_(false) + : data_(std::move(s)) {} charset_string(const std::string& s, unicode encoding) @@ -1067,12 +1162,21 @@ namespace nana { switch(encoding) { +#if defined(NANA_WINDOWS) + case unicode::utf8: + return utf16_to_utf8(wcstr); + case unicode::utf32: + return utf16_to_utf32(wcstr); + case unicode::utf16: + return wcstr; +#else //POSIX case unicode::utf8: return utf32_to_utf8(wcstr); case unicode::utf16: return utf32_to_utf16(wcstr); case unicode::utf32: return wcstr; +#endif } } return {}; @@ -1122,9 +1226,9 @@ namespace nana } private: std::string data_; - std::wstring wdata_for_move_; - bool is_unicode_; - unicode utf_x_; + std::wstring wdata_for_move_{}; + bool is_unicode_{ false }; + unicode utf_x_{ unicode::utf8 }; }; @@ -1195,7 +1299,7 @@ namespace nana } private: std::wstring data_; - std::string data_for_move_; + std::string data_for_move_{}; }; #endif } diff --git a/source/datetime.cpp b/source/datetime.cpp index 4682c32c..d633090d 100644 --- a/source/datetime.cpp +++ b/source/datetime.cpp @@ -17,62 +17,65 @@ #include namespace { - void localtime(struct tm& tm) + std::tm localtime() { #if defined(NANA_WINDOWS) && !defined(NANA_MINGW) time_t t; ::time(&t); + std::tm tm; if(localtime_s(&tm, &t) != 0) - { assert(false); - } + + return tm; #else time_t t = std::time(nullptr); struct tm * tm_addr = std::localtime(&t); assert(tm_addr); - tm = *tm_addr; + + return *tm_addr; #endif } + + ::nana::date::value to_dateval(const std::tm& t) + { + return {static_cast(t.tm_year + 1900), static_cast(t.tm_mon + 1), static_cast(t.tm_mday)}; + } + + ::nana::time::value to_timeval(const std::tm& t) + { + return {static_cast(t.tm_hour), static_cast(t.tm_min), static_cast(t.tm_sec)}; + } + } // namespace anonymous namespace nana { - //class date + //class date void date::set(const std::tm& t) { - value_.year = t.tm_year + 1900; - value_.month = t.tm_mon + 1; - value_.day = t.tm_mday; + value_ = to_dateval(t); } date::date() + : value_(to_dateval(localtime())) { - struct tm t; - localtime(t); - set(t); } date::date(const std::tm& t) + : value_(to_dateval(t)) { - set(t); } date::date(int year, int month, int day) + : value_({static_cast(year), static_cast(month), static_cast(day)}) { if(1601 <= year && year < 30827 && 0 < month && month < 13 && day > 0) { if(day <= static_cast(date::month_days(year, month))) - { - value_.year = year; - value_.month = month; - value_.day = day; return; - } } - struct tm t; - localtime(t); - set(t); + set(localtime()); } date date::operator - (int off) const @@ -258,39 +261,27 @@ namespace nana //class time void time::set(const std::tm& t) { - value_.hour = t.tm_hour; - value_.minute = t.tm_min; - value_.second = t.tm_sec; + value_ = to_timeval(t); } time::time() + : value_(to_timeval(localtime())) { - struct tm t; - localtime(t); - set(t); } time::time(const std::tm& t) + : value_(to_timeval(t)) { - value_.hour = t.tm_hour; - value_.minute = t.tm_min; - value_.second = t.tm_sec; } time::time(unsigned hour, unsigned minute, unsigned second) + : value_({hour, minute, second}) { if(hour < 24 && minute < 60 && second < 62) - { - value_.hour = hour; - value_.minute = minute; - value_.second = second; return; - } - struct tm t; - localtime(t); - set(t); - } + set(localtime()); + } const time::value& time::read() const { return value_; diff --git a/source/deploy.cpp b/source/deploy.cpp index 3592a3bf..7f715ef9 100644 --- a/source/deploy.cpp +++ b/source/deploy.cpp @@ -436,9 +436,54 @@ namespace std } #endif +//#ifdef STD_put_time_NOT_SUPPORTED +#include +#include +namespace std +{ + //Workaround for no implemenation of std::put_time in gcc < 5. + /* std unspecified return type */ + //template< class CharT, class RTSTR >// let fail for CharT != char / wchar_t + //RTSTR put_time(const std::tm* tmb, const CharT* fmt); + + //template< > + std::string put_time/**/(const std::tm* tmb, const char* fmt) + { + std::size_t sz = 200; + std::string str(sz, '\0'); + sz = std::strftime(&str[0], str.size() - 1, fmt, tmb); + str.resize(sz); + return str; + } + //Defined in header + // std::size_t strftime(char* str, std::size_t count, const char* format, const std::tm* time); + //template<> + //std::wstring put_time(const std::tm* tmb, const wchar_t* fmt) + //{ + // unsigned sz = 200; + // std::wstring str(sz, L'\0'); + // sz = std::wcsftime(&str[0], str.size() - 1, fmt, tmb); + // str.resize(sz); + // return str; + //} + // http://en.cppreference.com/w/cpp/chrono/c/wcsftime + // Defined in header + // std::size_t wcsftime(wchar_t* str, std::size_t count, const wchar_t* format, const std::tm* time); + // Converts the date and time information from a given calendar time time to a null - terminated + // wide character string str according to format string format.Up to count bytes are written. + // Parameters + // str - pointer to the first element of the wchar_t array for output + // count - maximum number of wide characters to write + // format - pointer to a null - terminated wide character string specifying the format of conversion. + + } +//#endif // STD_put_time_NOT_SUPPORTED + +#include + namespace nana { - bool is_utf8(const char* str, unsigned len) + bool is_utf8(const char* str, std::size_t len) { auto ustr = reinterpret_cast(str); auto end = ustr + len; @@ -455,38 +500,95 @@ namespace nana if (uv < 0xC0) return false; - if ((uv < 0xE0) && (ustr + 1 < end)) + if ((uv < 0xE0) && (end - ustr > 1)) ustr += 2; - else if (uv < 0xF0 && (ustr + 2 <= end)) + else if ((uv < 0xF0) && (end - ustr > 2)) ustr += 3; - else if (uv < 0x1F && (ustr + 3 <= end)) + else if ((uv < 0x1F) && (end - ustr > 3)) ustr += 4; else return false; } - return true; } + //class utf8_Error + +#if defined(_MSC_VER) +# if (_MSC_VER < 1900) + //A workaround for lack support of C++11 inheriting constructors for VC2013 + utf8_Error::utf8_Error(const std::string& msg) + : std::runtime_error(msg) + {} +# endif +#endif + + void utf8_Error::emit() + { + if (use_throw) + throw utf8_Error(*this); + std::cerr << what(); + } + + //bool utf8_Error::use_throw{true}; + bool utf8_Error::use_throw{ false }; + //end class utf8_Error + void throw_not_utf8(const std::string& text) { if (!is_utf8(text.c_str(), text.length())) - throw std::invalid_argument("The text is not encoded in UTF8"); + return utf8_Error(std::string("\nThe text is not encoded in UTF8: ") + text).emit(); } - void throw_not_utf8(const char* text, unsigned len) + void throw_not_utf8(const char* text, std::size_t len) { if (!is_utf8(text, len)) - throw std::invalid_argument("The text is not encoded in UTF8"); + return utf8_Error(std::string("\nThe text is not encoded in UTF8: ") + std::string(text, len) ).emit(); + + //throw std::invalid_argument("The text is not encoded in UTF8"); } void throw_not_utf8(const char* text) { if (!is_utf8(text, std::strlen(text))) - throw std::invalid_argument("The text is not encoded in UTF8"); + return utf8_Error(std::string("\nThe text is not encoded in UTF8: ") + text).emit(); + + //throw std::invalid_argument("The text is not encoded in UTF8"); } + std::string recode_to_utf8(std::string no_utf8) + { + return nana::charset(std::move(no_utf8)).to_bytes(nana::unicode::utf8); + } + + /// this text needed change, it needed review ?? + bool review_utf8(const std::string& text) + { + if (!is_utf8(text.c_str(), text.length())) + { + utf8_Error(std::string("\nThe const text is not encoded in UTF8: ") + text).emit(); + return true; /// it needed change, it needed review !! + } + else + return false; + } + + /// this text needed change, it needed review ?? + bool review_utf8(std::string& text) + { + if (!is_utf8(text.c_str(), text.length())) + { + utf8_Error(std::string("\nThe text is not encoded in UTF8: ") + text).emit(); + text=recode_to_utf8(text); + return true; /// it needed change, it needed review !! + } + else + return false; + } + + + const std::string& to_utf8(const std::string& str) { return str; @@ -599,3 +701,6 @@ namespace nana } +#if defined(VERBOSE_PREPROCESSOR) +# include +#endif \ No newline at end of file diff --git a/source/detail/platform_spec_posix.cpp b/source/detail/platform_spec_posix.cpp index 9713a0c9..a2121991 100644 --- a/source/detail/platform_spec_posix.cpp +++ b/source/detail/platform_spec_posix.cpp @@ -16,6 +16,9 @@ #include #if defined(NANA_POSIX) && defined(NANA_X11) + +#include + #include #include #include @@ -432,13 +435,13 @@ namespace detail platform_spec::instance().unlock_xlib(); } - int X11_error_handler(Display* disp, XErrorEvent* err) + int X11_error_handler(Display*, XErrorEvent* err) { platform_spec::instance().error_code = err->error_code; return 0; } - int X11_fatal_handler(Display* disp) + int X11_fatal_handler(Display*) { return 0; } @@ -1414,4 +1417,6 @@ namespace detail } }//end namespace detail }//end namespace nana + +#include #endif //NANA_POSIX && NANA_X11 diff --git a/source/detail/platform_spec_windows.cpp b/source/detail/platform_spec_windows.cpp index 88317ede..7d1ae510 100644 --- a/source/detail/platform_spec_windows.cpp +++ b/source/detail/platform_spec_windows.cpp @@ -1,7 +1,7 @@ -/* +/** * Platform Specification Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -9,7 +9,7 @@ * * @file: nana/detail/platform_spec.cpp * - * This file provides basis class and data structrue that required by nana + * @brief basis classes and data structures required by nana */ #include @@ -19,9 +19,172 @@ #include #include -#if defined(_MSC_VER) -#include -#endif // _MSVC + + +/////////////////////////////////////////////////////////////////////////////////////////////////////// + +/****************************************************************** +* * +* VersionHelpers.h -- This module defines helper functions to * +* promote version check with proper * +* comparisons. * +* * +* Copyright (c) Microsoft Corp. All rights reserved. * +* * +******************************************************************/ + +#include // for _In_, etc. + +#if !defined(__midl) && !defined(SORTPP_PASS) + +#if (NTDDI_VERSION >= NTDDI_WINXP) + +#ifdef __cplusplus + +#define VERSIONHELPERAPI inline bool + +#else // __cplusplus + +#define VERSIONHELPERAPI FORCEINLINE BOOL + +#endif // __cplusplus + +VERSIONHELPERAPI +IsWindowsVersionOrGreater(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor) +{ + OSVERSIONINFOEXW osvi = { sizeof(osvi), 0, 0, 0, 0,{ 0 }, 0, 0 }; + DWORDLONG const dwlConditionMask = VerSetConditionMask( + VerSetConditionMask( + VerSetConditionMask( + 0, VER_MAJORVERSION, VER_GREATER_EQUAL), + VER_MINORVERSION, VER_GREATER_EQUAL), + VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL); + + osvi.dwMajorVersion = wMajorVersion; + osvi.dwMinorVersion = wMinorVersion; + osvi.wServicePackMajor = wServicePackMajor; + + return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE; +} + +VERSIONHELPERAPI +IsWindowsXPOrGreater() +{ + return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 0); +} + +VERSIONHELPERAPI +IsWindowsXPSP1OrGreater() +{ + return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 1); +} + +VERSIONHELPERAPI +IsWindowsXPSP2OrGreater() +{ + return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 2); +} + +VERSIONHELPERAPI +IsWindowsXPSP3OrGreater() +{ + return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 3); +} + +VERSIONHELPERAPI +IsWindowsVistaOrGreater() +{ + return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), LOBYTE(_WIN32_WINNT_VISTA), 0); +} + +VERSIONHELPERAPI +IsWindowsVistaSP1OrGreater() +{ + return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), LOBYTE(_WIN32_WINNT_VISTA), 1); +} + +VERSIONHELPERAPI +IsWindowsVistaSP2OrGreater() +{ + return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), LOBYTE(_WIN32_WINNT_VISTA), 2); +} + +VERSIONHELPERAPI +IsWindows7OrGreater() +{ + return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN7), LOBYTE(_WIN32_WINNT_WIN7), 0); +} + +VERSIONHELPERAPI +IsWindows7SP1OrGreater() +{ + return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN7), LOBYTE(_WIN32_WINNT_WIN7), 1); +} + +#ifndef _WIN32_WINNT_WIN8 // (0x0602) + #define _WIN32_WINNT_WIN8 (0x0602) +#endif // _WIN32_WINNT_WIN8(0x0602) + +VERSIONHELPERAPI +IsWindows8OrGreater() +{ + + return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN8), LOBYTE(_WIN32_WINNT_WIN8), 0); +} + +#ifndef _WIN32_WINNT_WINBLUE // (0x0602) + #define _WIN32_WINNT_WINBLUE (0x0602) +#endif // _WIN32_WINNT_WINBLUE (0x0602) + +VERSIONHELPERAPI +IsWindows8Point1OrGreater() +{ + return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINBLUE), LOBYTE(_WIN32_WINNT_WINBLUE), 0); +} + +VERSIONHELPERAPI +IsWindowsServer() +{ + OSVERSIONINFOEXW osvi = { sizeof(osvi), 0, 0, 0, 0,{ 0 }, 0, 0, 0, VER_NT_WORKSTATION }; + DWORDLONG const dwlConditionMask = VerSetConditionMask(0, VER_PRODUCT_TYPE, VER_EQUAL); + + return !VerifyVersionInfoW(&osvi, VER_PRODUCT_TYPE, dwlConditionMask); +} + +#endif // NTDDI_VERSION + +#endif // defined(__midl) + + + +//////////////////////////////////////////////////////////////////////////////////////////////////// + + + +//#if defined(_MSC_VER) +////#include +//bool IsWindowsVistaOrGreater() { return false; } +//bool //VERSIONHELPERAPI +//IsWindowsVersionOrGreater(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor) +//{ +// OSVERSIONINFOEXW osvi = { sizeof(osvi), 0, 0, 0, 0,{ 0 }, 0, 0 }; +// DWORDLONG const dwlConditionMask = VerSetConditionMask( +// VerSetConditionMask( +// VerSetConditionMask( +// 0, VER_MAJORVERSION, VER_GREATER_EQUAL), +// VER_MINORVERSION, VER_GREATER_EQUAL), +// VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL); +// +// osvi.dwMajorVersion = wMajorVersion; +// osvi.dwMinorVersion = wMinorVersion; +// osvi.wServicePackMajor = wServicePackMajor; +// +// return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE; +//} +// +// +// +//#endif // _MSVC namespace nana { diff --git a/source/detail/x11/msg_dispatcher.hpp b/source/detail/x11/msg_dispatcher.hpp index 7ba64b4f..2f1688e0 100644 --- a/source/detail/x11/msg_dispatcher.hpp +++ b/source/detail/x11/msg_dispatcher.hpp @@ -1,6 +1,6 @@ /* * Message Dispatcher Implementation - * Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -164,7 +164,7 @@ namespace detail private: void _m_msg_driver() { - int fd_X11 = ConnectionNumber(display_); + const int fd_X11 = ConnectionNumber(display_); msg_packet_tag msg_pack; XEvent event; @@ -177,6 +177,18 @@ namespace detail if(pending) { ::XNextEvent(display_, &event); + + if(KeyRelease == event.type) + { + //Check whether the key is pressed, because X will send KeyRelease when pressing and + //holding a key if auto repeat is on. + char keymap[32]; + ::XQueryKeymap(display_, keymap); + + if(keymap[event.xkey.keycode / 8] & (1 << (event.xkey.keycode % 8))) + continue; + } + if(::XFilterEvent(&event, None)) continue; } diff --git a/source/filesystem/filesystem.cpp b/source/filesystem/filesystem.cpp index aca26162..745a52a2 100644 --- a/source/filesystem/filesystem.cpp +++ b/source/filesystem/filesystem.cpp @@ -11,8 +11,11 @@ * provide some interface for file managment */ -#include +#include #include +#include +#include //put_time + #if defined(NANA_WINDOWS) #include @@ -36,13 +39,134 @@ #include #endif +namespace fs = std::experimental::filesystem; -namespace nana { namespace experimental { -#ifndef CXX_NO_INLINE_NAMESPACE - inline namespace v1 { -#endif - namespace filesystem +namespace nana +{ + namespace filesystem_ext { + + fs::path path_user() + { +#if defined(NANA_WINDOWS) + wchar_t pstr[MAX_PATH]; + if (SUCCEEDED(SHGetFolderPath(0, CSIDL_PROFILE, 0, SHGFP_TYPE_CURRENT, pstr))) + return pstr; +#elif defined(NANA_LINUX) || defined(NANA_MACOS) + const char * pstr = ::getenv("HOME"); + if (pstr) + return pstr; +#endif + return fs::path(); + } + + std::string pretty_file_size(const fs::path& path) + { + try { + auto bytes = fs::file_size(path); + const char * ustr[] = { " KB", " MB", " GB", " TB" }; + std::stringstream ss; + if (bytes < 1024) + ss << bytes << " Bytes"; + else + { + double cap = bytes / 1024.0; + std::size_t uid = 0; + while ((cap >= 1024.0) && (uid < sizeof(ustr) / sizeof(char *))) + { + cap /= 1024.0; + ++uid; + } + ss << cap; + auto s = ss.str(); + auto pos = s.find('.'); + if (pos != s.npos) + { + if (pos + 2 < s.size()) + s.erase(pos + 2); + } + return s + ustr[uid]; + } + + return ss.str(); + } + catch (...) {} + return{}; + } + + std::string pretty_file_date(const fs::path& path) // todo: move to .cpp + { + try { + auto ftime = fs::last_write_time(path); + + // crash: VS2015 will not read the time for some files (for example: C:/hiberfil.sys) + // and will return file_time_type(-1) without throwing + // https://msdn.microsoft.com/en-us/library/dn823784.aspx + + if (ftime == ((fs::file_time_type::min)())) return{}; + + //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); + + std::stringstream tm; + tm << std::put_time(std::localtime(&cftime), "%Y-%m-%d, %H:%M:%S"); + return tm.str(); + } + catch (...) { + return{}; + } + } + + bool modified_file_time(const fs::path& p, struct tm& t) + { +#if defined(NANA_WINDOWS) + WIN32_FILE_ATTRIBUTE_DATA attr; + if (::GetFileAttributesEx(p.c_str(), GetFileExInfoStandard, &attr)) + { + FILETIME local_file_time; + if (::FileTimeToLocalFileTime(&attr.ftLastWriteTime, &local_file_time)) + { + SYSTEMTIME st; + ::FileTimeToSystemTime(&local_file_time, &st); + t.tm_year = st.wYear - 1900; + t.tm_mon = st.wMonth - 1; + t.tm_mday = st.wDay; + t.tm_wday = st.wDayOfWeek - 1; + t.tm_yday = nana::date::day_in_year(st.wYear, st.wMonth, st.wDay); + + t.tm_hour = st.wHour; + t.tm_min = st.wMinute; + t.tm_sec = st.wSecond; + return true; + } + } +#elif defined(NANA_POSIX) + struct stat attr; + if (0 == ::stat(p.c_str(), &attr)) + { + t = *(::localtime(&attr.st_ctime)); + return true; + } +#endif + return false; + } + } +} + +#if NANA_USING_NANA_FILESYSTEM + +namespace nana_fs = nana::experimental::filesystem; + +namespace nana { namespace experimental { namespace filesystem + { +#ifndef CXX_NO_INLINE_NAMESPACE + inline namespace v1 { +#endif + //class filesystem_error filesystem_error::filesystem_error(const std::string& msg, std::error_code err) : std::system_error(err, msg) @@ -59,12 +183,12 @@ namespace nana { namespace experimental { path2_(path2) {} - const path& filesystem_error::path1() const + const path& filesystem_error::path1() const noexcept { return path1_; } - const path&filesystem_error::path2() const + const path& filesystem_error::path2() const noexcept { return path2_; } @@ -110,7 +234,8 @@ namespace nana { namespace experimental { return pathstr_.compare(p.pathstr_); } - bool path::empty() const + /// 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); @@ -122,24 +247,27 @@ namespace nana { namespace experimental { path path::extension() const { + // todo: make more globlal #if defined(NANA_WINDOWS) - auto pos = pathstr_.find_last_of(L"\\/."); + auto SLorP=L"\\/."; + auto P=L'.'; #else - auto pos = pathstr_.find_last_of("\\/."); + auto SLorP="\\/."; + auto P='.'; #endif - if ((pos == pathstr_.npos) || (pathstr_[pos] != '.')) - return path(); + auto pos = pathstr_.find_last_of(SLorP); - - if (pos + 1 == pathstr_.size()) - return path(); + if ( ( pos == pathstr_.npos) + || ( pathstr_[pos] != P ) + || ( pos + 1 == pathstr_.size() )) + return path(); return path(pathstr_.substr(pos)); } path path::parent_path() const { - return{filesystem::parent_path(pathstr_)}; + return{nana_fs::parent_path(pathstr_)}; } file_type path::what() const @@ -338,17 +466,17 @@ namespace nana { namespace experimental { } //class directory_entry - directory_entry::directory_entry(const filesystem::path& p) + directory_entry::directory_entry(const nana_fs::path& p) :path_{ p } {} //modifiers - void directory_entry::assign(const filesystem::path& p) + void directory_entry::assign(const nana_fs::path& p) { path_ = p; } - void directory_entry::replace_filename(const filesystem::path& p) + void directory_entry::replace_filename(const nana_fs::path& p) { path_ = path_.parent_path() / p; } @@ -356,15 +484,15 @@ namespace nana { namespace experimental { //observers file_status directory_entry::status() const { - return filesystem::status(path_); + return nana_fs::status(path_); } - directory_entry::operator const filesystem::path&() const - { - return path_; - } + //directory_entry::operator const nana_fs::path&() const + //{ + // return path_; + //} - const path& directory_entry::path() const + const nana_fs::path& directory_entry::path() const { return path_; } @@ -384,7 +512,7 @@ namespace nana { namespace experimental { } }; - directory_iterator::directory_iterator() + directory_iterator::directory_iterator() noexcept : end_(true), handle_(nullptr) {} @@ -414,14 +542,10 @@ namespace nana { namespace experimental { bool directory_iterator::equal(const directory_iterator& x) const { if (end_ && (end_ == x.end_)) return true; - return (value_.path().filename() == x.value_.path().filename()); + return (value_.path().filename() == x.value_.path().filename()); } - // enable directory_iterator range-based for statements - directory_iterator directory_iterator::begin() { return *this; } - directory_iterator directory_iterator::end() { return{}; } - void directory_iterator::_m_prepare(const path& file_path) { path_ = file_path.native(); @@ -751,41 +875,15 @@ namespace nana { namespace experimental { #endif } - bool modified_file_time(const path& p, struct tm& t) + + file_time_type last_write_time(const path& p) { -#if defined(NANA_WINDOWS) - WIN32_FILE_ATTRIBUTE_DATA attr; - if (::GetFileAttributesEx(p.c_str(), GetFileExInfoStandard, &attr)) - { - FILETIME local_file_time; - if (::FileTimeToLocalFileTime(&attr.ftLastWriteTime, &local_file_time)) - { - SYSTEMTIME st; - ::FileTimeToSystemTime(&local_file_time, &st); - t.tm_year = st.wYear - 1900; - t.tm_mon = st.wMonth - 1; - t.tm_mday = st.wDay; - t.tm_wday = st.wDayOfWeek - 1; - t.tm_yday = nana::date::day_in_year(st.wYear, st.wMonth, st.wDay); - - t.tm_hour = st.wHour; - t.tm_min = st.wMinute; - t.tm_sec = st.wSecond; - return true; - } - } -#elif defined(NANA_POSIX) - struct stat attr; - if (0 == ::stat(p.c_str(), &attr)) - { - t = *(::localtime(&attr.st_ctime)); - return true; - } -#endif - return false; + struct tm t; + nana::filesystem_ext::modified_file_time(p, t); + std::chrono::system_clock::time_point dateTime =std::chrono::system_clock::from_time_t( mktime(&t) ); + return dateTime; } - bool create_directory(const path& p) { #if defined(NANA_WINDOWS) @@ -806,6 +904,7 @@ namespace nana { namespace experimental { bool remove(const path& p, std::error_code & ec) { + ec.clear(); auto stat = status(p); if (stat.type() == file_type::directory) return detail::rm_dir(p); @@ -813,20 +912,6 @@ namespace nana { namespace experimental { return detail::rm_file(p); } - path path_user() - { -#if defined(NANA_WINDOWS) - wchar_t pstr[MAX_PATH]; - if (SUCCEEDED(SHGetFolderPath(0, CSIDL_PROFILE, 0, SHGFP_TYPE_CURRENT, pstr))) - return pstr; -#elif defined(NANA_LINUX) || defined(NANA_MACOS) - const char * pstr = ::getenv("HOME"); - if (pstr) - return pstr; -#endif - return path(); - } - path current_path() { #if defined(NANA_WINDOWS) @@ -879,3 +964,5 @@ namespace nana { namespace experimental { }//end namespace filesystem } //end namespace experimental }//end namespace nana +#endif + diff --git a/source/gui/basis.cpp b/source/gui/basis.cpp index 28893fb4..e6742807 100644 --- a/source/gui/basis.cpp +++ b/source/gui/basis.cpp @@ -14,21 +14,38 @@ #include -namespace nana +using namespace nana; +using namespace nana::parameters; + +//struct appearance +appearance::appearance() + :taskbar(true), floating(false), no_activate(false), + minimize(true), maximize(true), sizable(true), + decoration(true) +{} + +appearance::appearance(bool has_decorate, bool taskbar, bool is_float, bool no_activate, bool min, bool max, bool sizable) + : taskbar(taskbar), floating(is_float), no_activate(no_activate), + minimize(min), maximize(max), sizable(sizable), + decoration(has_decorate) +{} +//end struct appearance + +#if defined(NANA_WINDOWS) +# include +#endif + +mouse_wheel::mouse_wheel() + : lines(3), characters(3) { - //struct appearance - //@brief: Window appearance structure - appearance::appearance() - :taskbar(true), floating(false), no_activate(false), - minimize(true), maximize(true), sizable(true), - decoration(true) - {} - - appearance::appearance(bool has_decorate, bool taskbar, bool is_float, bool no_activate, bool min, bool max, bool sizable) - : taskbar(taskbar), floating(is_float), no_activate(no_activate), - minimize(min), maximize(max), sizable(sizable), - decoration(has_decorate) - {} - //end struct appearance -}//end namespace nana +#if defined(NANA_WINDOWS) + // https://msdn.microsoft.com/en-us/library/ms997498.aspx + //#define SPI_SETWHEELSCROLLCHARS 0x006D +# ifndef SPI_GETWHEELSCROLLCHARS +# define SPI_GETWHEELSCROLLCHARS 0x006C +# endif + ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &lines, 0); + ::SystemParametersInfo(SPI_GETWHEELSCROLLCHARS, 0, &characters, 0); +#endif +} diff --git a/source/gui/detail/basic_window.cpp b/source/gui/detail/basic_window.cpp index 9c1a5ae0..525f5e57 100644 --- a/source/gui/detail/basic_window.cpp +++ b/source/gui/detail/basic_window.cpp @@ -17,226 +17,243 @@ namespace nana { namespace detail { - //class caret_descriptor - caret_descriptor::caret_descriptor(core_window_t* wd, unsigned width, unsigned height) - :wd_(wd), size_(width, height), visible_state_(visible_state::invisible), out_of_range_(false) + //class caret + caret::caret(basic_window* owner, const size& size): + owner_(owner), + size_(size) {} - caret_descriptor::~caret_descriptor() + caret::~caret() { - if(wd_) native_interface::caret_destroy(wd_->root); + if (owner_) + native_interface::caret_destroy(owner_->root); } - void caret_descriptor::set_active(bool active) + void caret::activate(bool activity) { - if(wd_) + if (owner_) { - if(active) + if (activity) { - native_interface::caret_create(wd_->root, size_); + native_interface::caret_create(owner_->root, size_); - visible_state_ = visible_state::invisible; - this->position(point_.x, point_.y); + visibility_ = visible_state::invisible; + this->position(position_); } else - native_interface::caret_destroy(wd_->root); + native_interface::caret_destroy(owner_->root); - wd_->root_widget->other.attribute.root->ime_enabled = active; + owner_->root_widget->other.attribute.root->ime_enabled = activity; } } - auto caret_descriptor::window() const ->core_window_t* + basic_window* caret::owner() const noexcept { - return wd_; + return owner_; } - void caret_descriptor::position(int x, int y) + void caret::update() { - point_.x = x; - point_.y = y; + auto pos = position_; + auto size = size_; - update(); - } - - void caret_descriptor::effective_range(nana::rectangle rect) - { - //Chech rect - if (rect.width && rect.height && rect.right() > 0 && rect.bottom() > 0) - { - if(rect.x < 0) - { - rect.width += rect.x; - rect.x = 0; - } - - if(rect.y < 0) - { - rect.height += rect.y; - rect.y = 0; - } - - if(effective_range_ != rect) - { - effective_range_ = rect; - update(); - } - } - } - - nana::point caret_descriptor::position() const - { - return point_; - } - - void caret_descriptor::visible(bool is_show) - { - auto pre_displayed = (visible_state::displayed == visible_state_); - - if (is_show) - { - visible_state_ = visible_state::visible; - if (wd_->displayed() && (! out_of_range_)) - visible_state_ = visible_state::displayed; - } - else - visible_state_ = visible_state::invisible; - - if (pre_displayed != (visible_state::displayed == visible_state_)) - native_interface::caret_visible(wd_->root, !pre_displayed); - } - - bool caret_descriptor::visible() const - { - return (visible_state::invisible != visible_state_); - } - - nana::size caret_descriptor::size() const - { - return size_; - } - - void caret_descriptor::size(const nana::size& s) - { - size_ = s; - update(); - - if (visible_state::invisible != visible_state_) - visible(true); - } - - void caret_descriptor::update() - { - nana::point pos = point_; - nana::size size = size_; - - nana::rectangle rect = effective_range_; - if(0 == effective_range_.width || 0 == effective_range_.height) + auto rect = effect_range_; + if (0 == effect_range_.width || 0 == effect_range_.height) { rect.x = rect.y = 0; - rect = wd_->dimension; + rect.dimension(owner_->dimension); } else { - pos.x += effective_range_.x; - pos.y += effective_range_.y; + pos += effect_range_.position(); } - if( (pos.x + static_cast(size.width) <= rect.x) || (pos.x >= rect.right()) || + if ((pos.x + static_cast(size.width) <= rect.x) || (pos.x >= rect.right()) || (pos.y + static_cast(size.height) <= rect.y) || (pos.y >= rect.bottom()) ) {//Out of Range without overlap - if(false == out_of_range_) + if (false == out_of_range_) { out_of_range_ = true; - if (visible_state::invisible != visible_state_) + if (visible_state::invisible != visibility_) visible(false); } } else { - if(pos.x < rect.x) + if (pos.x < rect.x) { size.width -= (rect.x - pos.x); pos.x = rect.x; } - else if(pos.x + static_cast(size.width) > rect.right()) + else if (pos.x + static_cast(size.width) > rect.right()) { size.width -= pos.x + size.width - rect.right(); } - if(pos.y < rect.y) + if (pos.y < rect.y) { size.width -= (rect.y - pos.y); pos.y = rect.y; } - else if(pos.y + static_cast(size.height) > rect.bottom()) + else if (pos.y + static_cast(size.height) > rect.bottom()) size.height -= pos.y + size.height - rect.bottom(); - if(out_of_range_) + if (out_of_range_) { - if (paint_size_ == size) + if (visual_size_ == size) visible(true); out_of_range_ = false; } - if(paint_size_ != size) + if (visual_size_ != size) { - bool vs = (visible_state::invisible != visible_state_); - native_interface::caret_destroy(wd_->root); - native_interface::caret_create(wd_->root, size); + bool vs = (visible_state::invisible != visibility_); + native_interface::caret_destroy(owner_->root); + native_interface::caret_create(owner_->root, size); - visible_state_ = visible_state::invisible; + visibility_ = visible_state::invisible; if (vs) visible(true); - paint_size_ = size; + visual_size_ = size; } - - native_interface::caret_pos(wd_->root, wd_->pos_root + pos); + + native_interface::caret_pos(owner_->root, owner_->pos_root + pos); } } - //end class caret_descriptor + + //Implement caret_interface functions + void caret::disable_throw() noexcept + { + //This function is useless for class caret, see caret_proxy. + } + + void caret::effective_range(const rectangle& r) + { + auto range = r; + //Chech rect + if (range.width && range.height && range.right() > 0 && range.bottom() > 0) + { + if (range.x < 0) + { + range.width += range.x; + range.x = 0; + } + + if (range.y < 0) + { + range.height += range.y; + range.y = 0; + } + + if (effect_range_ != range) + { + effect_range_ = range; + update(); + } + } + } + + void caret::position(const point& pos) + { + position_ = pos; + update(); + } + + point caret::position() const + { + return position_; + } + + size caret::dimension() const + { + return size_; + } + + void caret::dimension(const size& s) + { + size_ = s; + update(); + + if (visible_state::invisible != visibility_) + visible(true); + } + + void caret::visible(bool visibility) + { + auto pre_displayed = (visible_state::displayed == visibility_); + + if (visibility) + { + visibility_ = visible_state::visible; + if (owner_->displayed() && (!out_of_range_)) + visibility_ = visible_state::displayed; + } + else + visibility_ = visible_state::invisible; + + if (pre_displayed != (visible_state::displayed == visibility_)) + native_interface::caret_visible(owner_->root, !pre_displayed); + } + + bool caret::visible() const + { + return (visible_state::invisible != visibility_); + } + //end class caret //struct basic_window //struct basic_window::other_tag 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::root_tag::value: + case category::flags::root: attribute.root = new attr_root_tag; break; - case category::frame_tag::value: + 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::root_tag::value: + case category::flags::root: delete attribute.root; break; - case category::frame_tag::value: + case category::flags::frame: delete attribute.frame; break; default: break; } +#endif + if (category::flags::root == category) + delete attribute.root; } //end struct basic_window::other_tag //basic_window //@brief: constructor for the root window basic_window::basic_window(basic_window* owner, std::unique_ptr&& wdg_notifier, category::root_tag**) - : widget_notifier(std::move(wdg_notifier)), other(category::root_tag::value) + : widget_notifier(std::move(wdg_notifier)), other(category::flags::root) { drawer.bind(this); _m_init_pos_and_size(nullptr, rectangle()); @@ -245,8 +262,8 @@ namespace nana basic_window::~basic_window() { - delete together.caret; - together.caret = nullptr; + delete annex.caret_ptr; + annex.caret_ptr = nullptr; delete effect.bground; effect.bground = nullptr; @@ -256,7 +273,7 @@ namespace nana //@brief: bind a native window and baisc_window void basic_window::bind_native_window(native_window_type wd, unsigned width, unsigned height, unsigned extra_width, unsigned extra_height, nana::paint::graphics& graphics) { - if(category::root_tag::value == this->other.category) + if(category::flags::root == this->other.category) { this->root = wd; dimension.width = width; @@ -268,11 +285,13 @@ namespace nana } } +#ifndef WIDGET_FRAME_DEPRECATED void basic_window::frame_window(native_window_type wd) { - if(category::frame_tag::value == this->other.category) + if(category::flags::frame == this->other.category) other.attribute.frame->container = wd; } +#endif bool basic_window::is_ancestor_of(const basic_window* wd) const { @@ -312,7 +331,7 @@ namespace nana const basic_window* get_child_caret(const basic_window* wd, bool this_is_a_child) { - if (this_is_a_child && wd->together.caret) + if (this_is_a_child && wd->annex.caret_ptr) return wd; for (auto child : wd->children) @@ -346,10 +365,16 @@ namespace nana return anc; } + void basic_window::set_action(mouse_action act) + { + flags.action_before = flags.action; + flags.action = act; + } + void basic_window::_m_init_pos_and_size(basic_window* parent, const rectangle& r) { - pos_owner = pos_root = r; - dimension = r; + pos_owner = pos_root = r.position(); + dimension = r.dimension(); if (parent) pos_root += parent->pos_root; @@ -357,12 +382,12 @@ namespace nana void basic_window::_m_initialize(basic_window* agrparent) { - if(other.category == category::root_tag::value) + if(category::flags::root == other.category) { if(agrparent && (nana::system::this_thread_id() != agrparent->thread_id)) agrparent = nullptr; - while(agrparent && (agrparent->other.category != category::root_tag::value)) + while(agrparent && (category::flags::root != agrparent->other.category)) agrparent = agrparent->parent; owner = agrparent; @@ -390,6 +415,8 @@ namespace nana flags.fullscreen = false; flags.tab = nana::detail::tab_type::none; flags.action = mouse_action::normal; + flags.action_before = mouse_action::normal; + flags.refreshing = false; flags.destroying = false; flags.borderless = false; @@ -404,8 +431,6 @@ namespace nana effect.bground = nullptr; effect.bground_fade_rate = 0; - together.caret = nullptr; - extra_width = extra_height = 0; //The window must keep its thread_id same as its parent if it is a child. @@ -417,15 +442,15 @@ namespace nana bool basic_window::set_events(const std::shared_ptr& p) { - if (together.events_ptr) + if (annex.events_ptr) return false; - together.events_ptr = p; + annex.events_ptr = p; return true; } general_events * basic_window::get_events() const { - return together.events_ptr.get(); + return annex.events_ptr.get(); } //end struct basic_window }//end namespace detail diff --git a/source/gui/detail/bedrock_pi.cpp b/source/gui/detail/bedrock_pi.cpp index 830887ea..c0e37ae4 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-2015 Jinhao(cnjinhao@hotmail.com) +* Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -36,6 +36,18 @@ namespace nana detail::bedrock::instance().wd_manager().internal_lock().unlock(); } //end class internal_scope_guard + + //class internal_revert_guard + internal_revert_guard::internal_revert_guard() + { + detail::bedrock::instance().wd_manager().internal_lock().revert(); + } + + internal_revert_guard::~internal_revert_guard() + { + detail::bedrock::instance().wd_manager().internal_lock().forward(); + } + //end class internal_revert_guard //class event_arg void event_arg::stop_propagation() const @@ -51,16 +63,16 @@ namespace nana namespace detail { + bool check_window(window wd) + { + return bedrock::instance().wd_manager().available(reinterpret_cast(wd)); + } + void events_operation_register(event_handle evt) { bedrock::instance().evt_operation().register_evt(evt); } - void events_operation_cancel(event_handle evt) - { - bedrock::instance().evt_operation().cancel(evt); - } - class bedrock::flag_guard { public: @@ -117,16 +129,16 @@ namespace nana arg.window_handle = reinterpret_cast(wd); if (emit(event_code::expose, wd, arg, false, get_thread_context())) { - const core_window_t * caret_wd = (wd->together.caret ? wd : wd->child_caret()); + const core_window_t * caret_wd = (wd->annex.caret_ptr ? wd : wd->child_caret()); if (caret_wd) { if (exposed) { if (wd->root_widget->other.attribute.root->focus == caret_wd) - caret_wd->together.caret->visible(true); + caret_wd->annex.caret_ptr->visible(true); } else - caret_wd->together.caret->visible(false); + caret_wd->annex.caret_ptr->visible(false); } if (!exposed) @@ -136,8 +148,10 @@ namespace nana //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 } wd_manager().refresh_tree(wd); @@ -153,8 +167,7 @@ namespace nana arg.window_handle = reinterpret_cast(wd); arg.x = x; arg.y = y; - if (emit(event_code::move, wd, arg, false, get_thread_context())) - wd_manager().update(wd, false, true); + emit(event_code::move, wd, arg, true, get_thread_context()); } } @@ -162,7 +175,7 @@ namespace nana { if (wd_manager().available(hovered) && hovered->flags.enabled) { - hovered->flags.action = mouse_action::normal; + hovered->set_action(mouse_action::normal); arg_mouse arg; arg.evt_code = event_code::mouse_leave; @@ -176,6 +189,38 @@ namespace nana return false; } + //The wd must be a root window + void bedrock::event_focus_changed(core_window_t* root_wd, native_window_type receiver, bool getting) + { + auto focused = root_wd->other.attribute.root->focus; + + arg_focus arg; + arg.window_handle = reinterpret_cast(focused); + arg.getting = getting; + arg.receiver = receiver; + + if (getting) + { + if (root_wd->flags.enabled && root_wd->flags.take_active) + { + if (focused && focused->annex.caret_ptr) + focused->annex.caret_ptr->activate(true); + + if (!emit(event_code::focus, focused, arg, true, get_thread_context())) + this->wd_manager().set_focus(root_wd, true, arg_focus::reason::general); + } + } + else if (root_wd->other.attribute.root->focus) + { + if (emit(event_code::focus, focused, arg, true, get_thread_context())) + { + if (focused->annex.caret_ptr) + focused->annex.caret_ptr->activate(false); + } + close_menu_if_focus_other_window(receiver); + } + } + void bedrock::update_cursor(core_window_t * wd) { internal_scope_guard isg; @@ -198,36 +243,140 @@ namespace nana } } - widget_colors& bedrock::get_scheme_template(scheme_factory_base&& factory) + void bedrock::set_menubar_taken(core_window_t* wd) { - return pi_data_->scheme.scheme_template(std::move(factory)); + auto pre = pi_data_->menu.taken_window; + pi_data_->menu.taken_window = wd; + + //assigning of a nullptr taken window is to restore the focus of pre taken + //don't restore the focus if pre is a menu. + if ((!wd) && pre && (pre->root != get_menu())) + { + internal_scope_guard lock; + wd_manager().set_focus(pre, false, arg_focus::reason::general); + wd_manager().update(pre, true, false); + } } - widget_colors* bedrock::make_scheme(scheme_factory_base&& factory) + //0:Enable delay, 1:Cancel, 2:Restores, 3: Restores when menu is destroying + void bedrock::delay_restore(int state) { - return pi_data_->scheme.create(std::move(factory)); + switch (state) + { + case 0: //Enable + break; + case 1: //Cancel + break; + case 2: //Restore if key released + //restores the focus when menu is closed by pressing keyboard + if ((!pi_data_->menu.window) && pi_data_->menu.delay_restore) + set_menubar_taken(nullptr); + break; + case 3: //Restores if destroying + //when the menu is destroying, restores the focus if delay restore is not declared + if (!pi_data_->menu.delay_restore) + set_menubar_taken(nullptr); + } + + pi_data_->menu.delay_restore = (0 == state); + } + + bool bedrock::close_menu_if_focus_other_window(native_window_type wd) + { + if (pi_data_->menu.window && (pi_data_->menu.window != wd)) + { + wd = native_interface::get_owner_window(wd); + while (wd) + { + if (wd != pi_data_->menu.window) + wd = native_interface::get_owner_window(wd); + else + return false; + } + erase_menu(true); + return true; + } + return false; + } + + void bedrock::set_menu(native_window_type menu_wd, bool has_keyboard) + { + if(menu_wd && pi_data_->menu.window != menu_wd) + { + erase_menu(true); + + pi_data_->menu.window = menu_wd; + pi_data_->menu.owner = native_interface::get_owner_window(menu_wd); + pi_data_->menu.has_keyboard = has_keyboard; + } + } + + native_window_type bedrock::get_menu(native_window_type owner, bool is_keyboard_condition) + { + if ((pi_data_->menu.owner == nullptr) || + (owner && (pi_data_->menu.owner == owner)) + ) + { + return (is_keyboard_condition ? (pi_data_->menu.has_keyboard ? pi_data_->menu.window : nullptr) : pi_data_->menu.window); + } + + return nullptr; + } + + native_window_type bedrock::get_menu() + { + return pi_data_->menu.window; + } + + void bedrock::erase_menu(bool try_destroy) + { + if (pi_data_->menu.window) + { + if (try_destroy) + native_interface::close_window(pi_data_->menu.window); + + pi_data_->menu.window = pi_data_->menu.owner = nullptr; + pi_data_->menu.has_keyboard = false; + } + } + + bool bedrock::shortkey_occurred(bool status) + { + auto last_status = pi_data_->shortkey_occurred; + pi_data_->shortkey_occurred = status; + return last_status; + } + + bool bedrock::shortkey_occurred() const + { + return pi_data_->shortkey_occurred; + } + + color_schemes& bedrock::scheme() + { + return pi_data_->scheme; } void bedrock::_m_emit_core(event_code evt_code, core_window_t* wd, bool draw_only, const ::nana::event_arg& event_arg) { - auto retain = wd->together.events_ptr; + auto retain = wd->annex.events_ptr; auto evts_ptr = retain.get(); - switch (evt_code) { case event_code::click: { auto arg = dynamic_cast(&event_arg); - if (nullptr == arg) - return; + if (arg) { - //enable refreshing flag, this is a RAII class for exception-safe - flag_guard fguard(this, wd); - wd->drawer.click(*arg); + { + //enable refreshing flag, this is a RAII class for exception-safe + flag_guard fguard(this, wd); + wd->drawer.click(*arg); + } + if (!draw_only) + evts_ptr->click.emit(*arg, reinterpret_cast(wd)); } - if (!draw_only) - evts_ptr->click.emit(*arg); } break; case event_code::dbl_click: @@ -281,7 +430,7 @@ namespace nana } if (!draw_only) - evt_addr->emit(*arg); + evt_addr->emit(*arg, reinterpret_cast(wd)); break; } case event_code::mouse_wheel: @@ -296,7 +445,7 @@ namespace nana } if (!draw_only) - evts_ptr->mouse_wheel.emit(*arg); + evts_ptr->mouse_wheel.emit(*arg, reinterpret_cast(wd)); } break; } @@ -340,7 +489,7 @@ namespace nana } if (!draw_only) - evt_addr->emit(*arg); + evt_addr->emit(*arg, reinterpret_cast(wd)); break; } case event_code::expose: @@ -348,7 +497,7 @@ namespace nana { auto arg = dynamic_cast(&event_arg); if (arg) - evts_ptr->expose.emit(*arg); + evts_ptr->expose.emit(*arg, reinterpret_cast(wd)); } break; case event_code::focus: @@ -362,7 +511,7 @@ namespace nana wd->drawer.focus(*arg); } if (!draw_only) - evts_ptr->focus.emit(*arg); + evts_ptr->focus.emit(*arg, reinterpret_cast(wd)); } break; } @@ -377,7 +526,7 @@ namespace nana wd->drawer.move(*arg); } if (!draw_only) - evts_ptr->move.emit(*arg); + evts_ptr->move.emit(*arg, reinterpret_cast(wd)); } break; } @@ -392,7 +541,7 @@ namespace nana wd->drawer.resizing(*arg); } if (!draw_only) - evts_ptr->resizing.emit(*arg); + evts_ptr->resizing.emit(*arg, reinterpret_cast(wd)); } break; } @@ -407,7 +556,7 @@ namespace nana wd->drawer.resized(*arg); } if (!draw_only) - evts_ptr->resized.emit(*arg); + evts_ptr->resized.emit(*arg, reinterpret_cast(wd)); } break; } @@ -419,7 +568,7 @@ namespace nana { auto evt_root = dynamic_cast(evts_ptr); if (evt_root) - evt_root->unload.emit(*arg); + evt_root->unload.emit(*arg, reinterpret_cast(wd)); } } break; @@ -428,7 +577,7 @@ namespace nana { auto arg = dynamic_cast(&event_arg); if (arg) - evts_ptr->destroy.emit(*arg); + evts_ptr->destroy.emit(*arg, reinterpret_cast(wd)); } break; default: diff --git a/source/gui/detail/bedrock_posix.cpp b/source/gui/detail/bedrock_posix.cpp index a8f78c37..c959b01c 100644 --- a/source/gui/detail/bedrock_posix.cpp +++ b/source/gui/detail/bedrock_posix.cpp @@ -97,27 +97,6 @@ namespace detail thread_context *object{ nullptr }; }tcontext; }cache; - - struct menu_tag - { - 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; - - struct keyboard_tracking_state_tag - { - keyboard_tracking_state_tag() - :has_shortkey_occured(false), has_keyup(true), alt(0) - {} - - bool has_shortkey_occured; - bool has_keyup; - - unsigned long alt : 2; - }keyboard_tracking_state; }; void timer_proc(unsigned); @@ -153,9 +132,9 @@ namespace detail delete impl_; } - void bedrock::map_thread_root_buffer(core_window_t*, bool forced, const rectangle*) + void bedrock::flush_surface(core_window_t* wd, bool forced, const rectangle* update_area) { - //GUI in X11 is thread-independent, so no implementation. + wd->drawer.map(reinterpret_cast(wd), forced, update_area); } //inc_window @@ -229,118 +208,12 @@ namespace detail return bedrock_object; } - category::flags bedrock::category(bedrock::core_window_t* wd) - { - if(wd) - { - internal_scope_guard lock; - if(wd_manager().available(wd)) - return wd->other.category; - } - return category::flags::super; - } - 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); } - void bedrock::set_menubar_taken(core_window_t* wd) - { - auto pre = impl_->menu.taken_window; - impl_->menu.taken_window = wd; - - //assigning of a nullptr taken window is to restore the focus of pre taken - if ((!wd) && pre) - { - internal_scope_guard lock; - wd_manager().set_focus(pre, false); - wd_manager().update(pre, true, false); - } - } - - //0:Enable delay, 1:Cancel, 2:Restores, 3: Restores when menu is destroying - void bedrock::delay_restore(int state) - { - switch (state) - { - case 0: //Enable - break; - case 1: //Cancel - break; - case 2: //Restore if key released - //restores the focus when menu is closed by pressing keyboard - if ((!impl_->menu.window) && impl_->menu.delay_restore) - set_menubar_taken(nullptr); - break; - case 3: //Restores if destroying - //when the menu is destroying, restores the focus if delay restore is not declared - if (!impl_->menu.delay_restore) - set_menubar_taken(nullptr); - } - - impl_->menu.delay_restore = (0 == state); - } - - bool bedrock::close_menu_if_focus_other_window(native_window_type wd) - { - if(impl_->menu.window && (impl_->menu.window != wd)) - { - wd = native_interface::get_owner_window(wd); - while(wd) - { - if(wd != impl_->menu.window) - wd = native_interface::get_owner_window(wd); - else - return false; - } - erase_menu(true); - return true; - } - return false; - } - - void bedrock::set_menu(native_window_type menu_window, bool has_keyboard) - { - if(menu_window && impl_->menu.window != menu_window) - { - erase_menu(true); - impl_->menu.window = menu_window; - impl_->menu.owner = native_interface::get_owner_window(menu_window); - impl_->menu.has_keyboard = has_keyboard; - } - } - - native_window_type bedrock::get_menu(native_window_type owner, bool is_keyboard_condition) - { - if( (impl_->menu.owner == nullptr) || - (owner && (impl_->menu.owner == owner)) - ) - { - return ( is_keyboard_condition ? (impl_->menu.has_keyboard ? impl_->menu.window : nullptr) : impl_->menu.window); - } - - return 0; - } - - native_window_type bedrock::get_menu() - { - return impl_->menu.window; - } - - void bedrock::erase_menu(bool try_destroy) - { - if (impl_->menu.window) - { - if (try_destroy) - native_interface::close_window(impl_->menu.window); - - impl_->menu.window = impl_->menu.owner = nullptr; - impl_->menu.has_keyboard = false; - } - } - void bedrock::get_key_state(arg_keyboard& arg) { XKeyEvent xkey; @@ -349,24 +222,12 @@ namespace detail arg.shift = (xkey.state & ShiftMask); } - bool bedrock::set_keyboard_shortkey(bool yes) - { - bool ret = impl_->keyboard_tracking_state.has_shortkey_occured; - impl_->keyboard_tracking_state.has_shortkey_occured = yes; - return ret; - } - - bool bedrock::whether_keyboard_shortkey() const - { - return impl_->keyboard_tracking_state.has_shortkey_occured; - } - element_store& bedrock::get_element_store() const { return impl_->estore; } - void bedrock::map_through_widgets(core_window_t* wd, native_drawable_type drawable) + void bedrock::map_through_widgets(core_window_t*, native_drawable_type) { //No implementation for Linux } @@ -389,38 +250,19 @@ namespace detail _m_emit_core(evt_code, wd, false, arg); - if(ask_update) - wd_manager().do_lazy_refresh(wd, false); - else + //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)) + { + wd_manager().do_lazy_refresh(wd, false, (event_code::resized == evt_code)); + } + else if(wd_manager().available(wd)) wd->other.upd_state = core_window_t::update_state::none; if(thrd) thrd->event_window = prev_wd; return true; } - bool bedrock::emit_drawer(event_code evt_code, core_window_t* wd, const ::nana::event_arg& arg, thread_context* thrd) - { - if(wd_manager().available(wd) == false) - return false; - - core_window_t * prev_wd; - if(thrd) - { - prev_wd = thrd->event_window; - thrd->event_window = wd; - _m_event_filter(evt_code, wd, thrd); - } - - if(wd->other.upd_state == core_window_t::update_state::none) - wd->other.upd_state = core_window_t::update_state::lazy; - - _m_emit_core(evt_code, wd, true, arg); - - if(thrd) thrd->event_window = prev_wd; - return true; - - } - void assign_arg(arg_mouse& arg, basic_window* wd, unsigned msg, const XEvent& evt) { arg.window_handle = reinterpret_cast(wd); @@ -479,6 +321,7 @@ namespace detail arg.window_handle = reinterpret_cast(wd); arg.receiver = recv; arg.getting = getting; + arg.focus_reason = arg_focus::reason::general; } void assign_arg(arg_wheel& arg, basic_window* wd, const XEvent& evt) @@ -519,7 +362,7 @@ namespace detail } } - void window_proc_for_packet(Display * display, nana::detail::msg_packet_tag& msg) + void window_proc_for_packet(Display * /*display*/, nana::detail::msg_packet_tag& msg) { static auto& brock = detail::bedrock::instance(); @@ -542,7 +385,7 @@ namespace detail delete msg.u.mouse_drop.files; arg.pos.x = msg.u.mouse_drop.x - msgwd->pos_root.x; arg.pos.y = msg.u.mouse_drop.y - msgwd->pos_root.y; - msgwd->together.events_ptr->mouse_dropfiles.emit(arg); + msgwd->annex.events_ptr->mouse_dropfiles.emit(arg, reinterpret_cast(msgwd)); brock.wd_manager().do_lazy_refresh(msgwd, false); } break; @@ -553,7 +396,7 @@ namespace detail } template - void emit_drawer(void(::nana::detail::drawer::*event_ptr)(const Arg&), basic_window* wd, const Arg& arg, bedrock::thread_context* thrd) + void draw_invoker(void(::nana::detail::drawer::*event_ptr)(const Arg&), basic_window* wd, const Arg& arg, bedrock::thread_context* thrd) { if(bedrock::instance().wd_manager().available(wd) == false) return; @@ -638,7 +481,7 @@ namespace detail return wchar_t(keysym); } - void window_proc_for_xevent(Display* display, XEvent& xevent) + void window_proc_for_xevent(Display* /*display*/, XEvent& xevent) { typedef detail::bedrock::core_window_t core_window_t; @@ -647,7 +490,8 @@ namespace detail static core_window_t* last_mouse_down_window; auto native_window = reinterpret_cast(event_window(xevent)); - auto root_runtime = brock.wd_manager().root_runtime(native_window); + auto & wd_manager = brock.wd_manager(); + auto root_runtime = wd_manager.root_runtime(native_window); if(root_runtime) { @@ -667,11 +511,12 @@ namespace detail if(pressed_wd_space) break; - msgwnd = brock.wd_manager().find_window(native_window, xevent.xcrossing.x, xevent.xcrossing.y); + msgwnd = wd_manager.find_window(native_window, xevent.xcrossing.x, xevent.xcrossing.y); if(msgwnd) { if (mouse_action::pressed != msgwnd->flags.action) - msgwnd->flags.action = mouse_action::over; + msgwnd->set_action(mouse_action::hovered); + hovered_wd = msgwnd; arg_mouse arg; @@ -681,7 +526,7 @@ namespace detail arg.evt_code = event_code::mouse_move; brock.emit(event_code::mouse_move, msgwnd, arg, true, &context); - if (!brock.wd_manager().available(hovered_wd)) + if (!wd_manager.available(hovered_wd)) hovered_wd = nullptr; } break; @@ -690,44 +535,22 @@ namespace detail hovered_wd = nullptr; break; case FocusIn: - if(msgwnd->flags.enabled && msgwnd->flags.take_active) - { - auto focus = msgwnd->other.attribute.root->focus; - if(focus && focus->together.caret) - focus->together.caret->set_active(true); - - arg_focus arg; - arg.window_handle = reinterpret_cast(focus); - arg.receiver = native_window; - arg.getting = true; - if(!brock.emit(event_code::focus, focus, arg, true, &context)) - brock.wd_manager().set_focus(msgwnd, true); - } + brock.event_focus_changed(msgwnd, native_window, true); break; case FocusOut: - if(msgwnd->other.attribute.root->focus && native_interface::is_window(msgwnd->root)) + if(native_interface::is_window(msgwnd->root)) { nana::point pos = native_interface::cursor_position(); auto recv = native_interface::find_window(pos.x, pos.y); - auto focus = msgwnd->other.attribute.root->focus; - arg_focus arg; - arg.window_handle = reinterpret_cast(focus); - arg.getting = false; - arg.receiver = recv; - if(brock.emit(event_code::focus, focus, arg, true, &context)) - { - if(focus->together.caret) - focus->together.caret->set_active(false); - } - brock.close_menu_if_focus_other_window(recv); + brock.event_focus_changed(msgwnd, recv, false); } break; case ConfigureNotify: if(msgwnd->dimension.width != static_cast(xevent.xconfigure.width) || msgwnd->dimension.height != static_cast(xevent.xconfigure.height)) { auto & cf = xevent.xconfigure; - brock.wd_manager().size(msgwnd, nana::size{static_cast(cf.width), static_cast(cf.height)}, true, true); + wd_manager.size(msgwnd, nana::size{static_cast(cf.width), static_cast(cf.height)}, true, true); } if(msgwnd->pos_native.x != xevent.xconfigure.x || msgwnd->pos_native.y != xevent.xconfigure.y) @@ -745,7 +568,7 @@ namespace detail if(xevent.xbutton.button == Button4 || xevent.xbutton.button == Button5) break; - msgwnd = brock.wd_manager().find_window(native_window, xevent.xbutton.x, xevent.xbutton.y); + msgwnd = wd_manager.find_window(native_window, xevent.xbutton.x, xevent.xbutton.y); if(nullptr == msgwnd) break; if ((msgwnd == msgwnd->root_widget->other.attribute.root->menubar) && brock.get_menu(msgwnd->root, true)) @@ -765,32 +588,34 @@ namespace detail if (new_focus && !new_focus->flags.ignore_mouse_focus) { context.event_window = new_focus; - auto kill_focus = brock.wd_manager().set_focus(new_focus, false); + auto kill_focus = brock.wd_manager().set_focus(new_focus, false, arg_focus::reason::mouse_press); if (kill_focus != new_focus) - brock.wd_manager().do_lazy_refresh(kill_focus, false); + wd_manager.do_lazy_refresh(kill_focus, false); } } - auto retain = msgwnd->together.events_ptr; + auto retain = msgwnd->annex.events_ptr; context.event_window = msgwnd; pressed_wd = nullptr; - msgwnd->flags.action = mouse_action::pressed; + + msgwnd->set_action(mouse_action::pressed); arg_mouse arg; assign_arg(arg, msgwnd, ButtonPress, xevent); arg.evt_code = dbl_click ? event_code::dbl_click : event_code::mouse_down; if(brock.emit(arg.evt_code, msgwnd, arg, true, &context)) { - if (brock.wd_manager().available(msgwnd)) + if (wd_manager.available(msgwnd)) { pressed_wd = msgwnd; //If a root window is created during the mouse_down event, Nana.GUI will ignore the mouse_up event. if (msgwnd->root != native_interface::get_focus_window()) { //call the drawer mouse up event for restoring the surface graphics - msgwnd->flags.action = mouse_action::normal; - emit_drawer(&drawer::mouse_up, msgwnd, arg, &context); - brock.wd_manager().do_lazy_refresh(msgwnd, false); + msgwnd->set_action(mouse_action::normal); + + draw_invoker(&drawer::mouse_up, msgwnd, arg, &context); + wd_manager.do_lazy_refresh(msgwnd, false); } } } @@ -807,7 +632,7 @@ namespace detail nana::point mspos{xevent.xbutton.x, xevent.xbutton.y}; while(msgwnd) { - if(msgwnd->together.events_ptr->mouse_wheel.length() != 0) + if(msgwnd->annex.events_ptr->mouse_wheel.length() != 0) { mspos -= msgwnd->pos_root; arg_wheel arg; @@ -821,14 +646,14 @@ namespace detail } else { - msgwnd = brock.wd_manager().find_window(native_window, xevent.xbutton.x, xevent.xbutton.y); + msgwnd = wd_manager.find_window(native_window, xevent.xbutton.x, xevent.xbutton.y); if(nullptr == msgwnd) break; - msgwnd->flags.action = mouse_action::normal; + msgwnd->set_action(mouse_action::normal); if(msgwnd->flags.enabled) { - auto retain = msgwnd->together.events_ptr; + auto retain = msgwnd->annex.events_ptr; ::nana::arg_mouse arg; assign_arg(arg, msgwnd, message, xevent); @@ -844,60 +669,58 @@ namespace detail { if((arg.button == ::nana::mouse::left_button) && hit) { - msgwnd->flags.action = mouse_action::over; + msgwnd->set_action(mouse_action::hovered); click_arg.window_handle = reinterpret_cast(msgwnd); - emit_drawer(&drawer::click, msgwnd, click_arg, &context); + draw_invoker(&drawer::click, msgwnd, click_arg, &context); } } //Do mouse_up, this handle may be closed by click handler. - if(brock.wd_manager().available(msgwnd) && msgwnd->flags.enabled) + if(wd_manager.available(msgwnd) && msgwnd->flags.enabled) { if(hit) - msgwnd->flags.action = mouse_action::over; + msgwnd->set_action(mouse_action::hovered); - auto retain = msgwnd->together.events_ptr; + auto retain = msgwnd->annex.events_ptr; auto evt_ptr = retain.get(); arg.evt_code = event_code::mouse_up; - emit_drawer(&drawer::mouse_up, msgwnd, arg, &context); + draw_invoker(&drawer::mouse_up, msgwnd, arg, &context); if(click_arg.window_handle) - evt_ptr->click.emit(click_arg); + evt_ptr->click.emit(click_arg, reinterpret_cast(msgwnd)); - if (brock.wd_manager().available(msgwnd)) + if (wd_manager.available(msgwnd)) { arg.evt_code = event_code::mouse_up; - evt_ptr->mouse_up.emit(arg); + evt_ptr->mouse_up.emit(arg, reinterpret_cast(msgwnd)); } } else if(click_arg.window_handle) - msgwnd->together.events_ptr->click.emit(click_arg); + msgwnd->annex.events_ptr->click.emit(click_arg, reinterpret_cast(msgwnd)); - brock.wd_manager().do_lazy_refresh(msgwnd, false); + wd_manager.do_lazy_refresh(msgwnd, false); } pressed_wd = nullptr; } break; case DestroyNotify: + if(wd_manager.available(msgwnd)) { - auto & spec = nana::detail::platform_spec::instance(); - if(brock.wd_manager().available(msgwnd)) + //The msgwnd may be destroyed if the window is destroyed by calling native interface of close_window(). + if (msgwnd->root == brock.get_menu()) { - //The msgwnd may be destroyed if the window is destroyed by calling native interface of close_window(). - if (msgwnd->root == brock.get_menu()) - { - brock.erase_menu(false); - brock.delay_restore(3); //Restores if delay_restore not decleared - } - - spec.remove(native_window); - brock.wd_manager().destroy(msgwnd); - - brock.manage_form_loader(msgwnd, false); - brock.wd_manager().destroy_handle(msgwnd); + brock.erase_menu(false); + brock.delay_restore(3); //Restores if delay_restore not decleared } + + auto & spec = ::nana::detail::platform_spec::instance(); + spec.remove(native_window); + wd_manager.destroy(msgwnd); + + brock.manage_form_loader(msgwnd, false); + wd_manager.destroy_handle(msgwnd); } break; case MotionNotify: @@ -915,35 +738,35 @@ namespace detail if(pressed_wd_space) break; - msgwnd = brock.wd_manager().find_window(native_window, xevent.xmotion.x, xevent.xmotion.y); - if (brock.wd_manager().available(hovered_wd) && (msgwnd != hovered_wd)) + msgwnd = wd_manager.find_window(native_window, xevent.xmotion.x, xevent.xmotion.y); + if (wd_manager.available(hovered_wd) && (msgwnd != hovered_wd)) { brock.event_msleave(hovered_wd); - hovered_wd->flags.action = mouse_action::normal; + hovered_wd->set_action(mouse_action::normal); hovered_wd = nullptr; //if msgwnd is neither a captured window nor a child of captured window, //redirect the msgwnd to the captured window. - auto cap_wd = brock.wd_manager().capture_redirect(msgwnd); + auto cap_wd = wd_manager.capture_redirect(msgwnd); if(cap_wd) msgwnd = cap_wd; } else if(msgwnd) { bool prev_captured_inside; - if(brock.wd_manager().capture_window_entered(xevent.xmotion.x, xevent.xmotion.y, prev_captured_inside)) + if(wd_manager.capture_window_entered(xevent.xmotion.x, xevent.xmotion.y, prev_captured_inside)) { event_code evt_code; if(prev_captured_inside) { evt_code = event_code::mouse_leave; - msgwnd->flags.action = mouse_action::normal; + msgwnd->set_action(mouse_action::normal); } else { evt_code = event_code::mouse_enter; if (mouse_action::pressed != msgwnd->flags.action) - msgwnd->flags.action = mouse_action::over; + msgwnd->set_action(mouse_action::hovered); } arg_mouse arg; assign_arg(arg, msgwnd, message, xevent); @@ -958,7 +781,7 @@ namespace detail assign_arg(arg, msgwnd, message, xevent); if (mouse_action::pressed != msgwnd->flags.action) - msgwnd->flags.action = mouse_action::over; + msgwnd->set_action(mouse_action::hovered); if (hovered_wd != msgwnd) { @@ -970,7 +793,7 @@ namespace detail arg.evt_code = event_code::mouse_move; brock.emit(event_code::mouse_move, msgwnd, arg, true, &context); } - if (!brock.wd_manager().available(hovered_wd)) + if (!wd_manager.available(hovered_wd)) hovered_wd = nullptr; break; case MapNotify: @@ -1035,7 +858,6 @@ namespace detail keybuf[len] = 0; wchar_t os_code = 0; - auto & wd_manager = brock.wd_manager(); switch(status) { case XLookupKeySym: @@ -1048,7 +870,8 @@ namespace detail auto tstop_wd = wd_manager.tabstop(msgwnd, !argkey.shift); if (tstop_wd) { - wd_manager.set_focus(tstop_wd, false); + root_runtime->condition.ignore_tab = true; + wd_manager.set_focus(tstop_wd, false, arg_focus::reason::tabstop); wd_manager.do_lazy_refresh(msgwnd, false); wd_manager.do_lazy_refresh(tstop_wd, true); } @@ -1069,19 +892,19 @@ namespace detail arg.pos.y = 0; arg.window_handle = reinterpret_cast(msgwnd); - msgwnd->flags.action = mouse_action::pressed; + msgwnd->set_action(mouse_action::pressed); pressed_wd_space = msgwnd; - auto retain = msgwnd->together.events_ptr; + auto retain = msgwnd->annex.events_ptr; - emit_drawer(&drawer::mouse_down, msgwnd, arg, &context); + draw_invoker(&drawer::mouse_down, msgwnd, arg, &context); wd_manager.do_lazy_refresh(msgwnd, false); } } else if(keyboard::alt == os_code) { context.is_alt_pressed = true; - if (brock.whether_keyboard_shortkey() == false) + if (brock.shortkey_occurred() == false) { msgwnd = msgwnd->root_widget->other.attribute.root->menubar; if (msgwnd) @@ -1116,7 +939,7 @@ namespace detail brock.emit(event_code::key_press, msgwnd, arg, true, &context); - if(brock.wd_manager().available(msgwnd) && (msgwnd->root_widget->other.attribute.root->menubar == msgwnd)) + if(wd_manager.available(msgwnd) && (msgwnd->root_widget->other.attribute.root->menubar == msgwnd)) { int cmd = (menu_wd && (keyboard::escape == static_cast(arg.key)) ? 1 : 0 ); brock.delay_restore(cmd); @@ -1144,15 +967,15 @@ namespace detail arg.ignore = false; arg.key = charbuf[i]; - // When tab is pressed, only tab-eating mode is allowed - if ((keyboard::tab == arg.key) && !(msgwnd->flags.tab & tab_type::eating)) + //Only accept tab when it is not ignored. + if ((keyboard::tab == arg.key) && root_runtime->condition.ignore_tab) continue; if(context.is_alt_pressed) { arg.ctrl = arg.shift = false; arg.evt_code = event_code::shortkey; - brock.set_keyboard_shortkey(true); + brock.shortkey_occurred(true); auto shr_wd = wd_manager.find_shortkey(native_window, arg.key); if(shr_wd) { @@ -1164,12 +987,12 @@ namespace detail arg.evt_code = event_code::key_char; arg.window_handle = reinterpret_cast(msgwnd); brock.get_key_state(arg); - msgwnd->together.events_ptr->key_char.emit(arg); + msgwnd->annex.events_ptr->key_char.emit(arg, reinterpret_cast(msgwnd)); if(arg.ignore == false && wd_manager.available(msgwnd)) - brock.emit_drawer(event_code::key_char, msgwnd, arg, &context); + draw_invoker(&drawer::key_char, msgwnd, arg, &context); } - if(brock.set_keyboard_shortkey(false)) + if(brock.shortkey_occurred(false)) context.is_alt_pressed = false; } break; @@ -1185,52 +1008,61 @@ namespace detail nana::detail::platform_spec::instance().write_keystate(xevent.xkey); { auto os_code = os_code_from_keysym(::XLookupKeysym(&xevent.xkey, 0)); - if(keyboard::alt != os_code) + if(keyboard::alt != os_code) //MUST NOT BE AN ALT { if(0x11 == os_code) context.is_ctrl_pressed = false; - msgwnd = brock.focus(); - if(msgwnd) - { - if(msgwnd == pressed_wd_space) - { - msgwnd->flags.action = mouse_action::normal; + if (('\t' == os_code) && root_runtime->condition.ignore_tab) + { + root_runtime->condition.ignore_tab = false; + } + else + { + + msgwnd = brock.focus(); + if(msgwnd) + { + if(msgwnd == pressed_wd_space) + { + msgwnd->set_action(mouse_action::normal); - arg_click click_arg; - click_arg.mouse_args = nullptr; - click_arg.window_handle = reinterpret_cast(msgwnd); + arg_click click_arg; + click_arg.mouse_args = nullptr; + click_arg.window_handle = reinterpret_cast(msgwnd); - auto retain = msgwnd->together.events_ptr; - if (brock.emit(event_code::click, msgwnd, click_arg, true, &context)) - { - arg_mouse arg; - arg.alt = false; - arg.button = ::nana::mouse::left_button; - arg.ctrl = false; - arg.evt_code = event_code::mouse_up; - arg.left_button = true; - arg.mid_button = false; - arg.pos.x = 0; - arg.pos.y = 0; - arg.window_handle = reinterpret_cast(msgwnd); + auto retain = msgwnd->annex.events_ptr; + if (brock.emit(event_code::click, msgwnd, click_arg, true, &context)) + { + arg_mouse arg; + arg.alt = false; + arg.button = ::nana::mouse::left_button; + arg.ctrl = false; + arg.evt_code = event_code::mouse_up; + arg.left_button = true; + arg.mid_button = false; + arg.pos.x = 0; + arg.pos.y = 0; + arg.window_handle = reinterpret_cast(msgwnd); - emit_drawer(&drawer::mouse_up, msgwnd, arg, &context); - brock.wd_manager().do_lazy_refresh(msgwnd, false); - } - pressed_wd_space = nullptr; - } - else - { - arg_keyboard arg; - arg.evt_code = event_code::key_release; - arg.window_handle = reinterpret_cast(msgwnd); - arg.ignore = false; - arg.key = os_code; - brock.get_key_state(arg); - brock.emit(event_code::key_release, msgwnd, arg, true, &context); - } - } + draw_invoker(&drawer::mouse_up, msgwnd, arg, &context); + wd_manager.do_lazy_refresh(msgwnd, false); + } + pressed_wd_space = nullptr; + } + else + { + arg_keyboard arg; + + arg.evt_code = event_code::key_release; + arg.window_handle = reinterpret_cast(msgwnd); + arg.ignore = false; + arg.key = os_code; + brock.get_key_state(arg); + brock.emit(event_code::key_release, msgwnd, arg, true, &context); + } + } + } if(os_code < keyboard::os_arrow_left || keyboard::os_arrow_down < os_code) brock.delay_restore(2); //Restores while key release @@ -1238,14 +1070,14 @@ namespace detail else { context.is_alt_pressed = false; - if (brock.set_keyboard_shortkey(false) == false) + if (brock.shortkey_occurred(false) == false) { msgwnd = msgwnd->root_widget->other.attribute.root->menubar; if (msgwnd) { bool set_focus = (brock.focus() != msgwnd) && (!msgwnd->root_widget->flags.ignore_menubar_focus); if (set_focus) - brock.wd_manager().set_focus(msgwnd, false); + wd_manager.set_focus(msgwnd, false, arg_focus::reason::general); arg_keyboard arg; arg.evt_code = event_code::key_release; @@ -1284,7 +1116,7 @@ namespace detail } } - root_runtime = brock.wd_manager().root_runtime(native_window); + root_runtime = wd_manager.root_runtime(native_window); if(root_runtime) { context.event_window = pre_event_window; @@ -1299,14 +1131,14 @@ namespace detail } auto thread_id = ::nana::system::this_thread_id(); - brock.wd_manager().call_safe_place(thread_id); + wd_manager.call_safe_place(thread_id); if(msgwnd) - brock.wd_manager().remove_trash_handle(thread_id); + wd_manager.remove_trash_handle(thread_id); } } - void bedrock::pump_event(window modal_window, bool is_modal) + void bedrock::pump_event(window modal_window, bool /*is_modal*/) { thread_context * context = open_thread_context(); if(0 == context->window_count) diff --git a/source/gui/detail/bedrock_windows.cpp b/source/gui/detail/bedrock_windows.cpp index 2915ec1c..d6019428 100644 --- a/source/gui/detail/bedrock_windows.cpp +++ b/source/gui/detail/bedrock_windows.cpp @@ -1,4 +1,4 @@ -/* +/** * A Bedrock Implementation * Nana C++ Library(http://www.nanapro.org) * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) @@ -7,7 +7,8 @@ * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) * - * @file: nana/gui/detail/win32/bedrock.cpp + * @file nana/gui/detail/win32/bedrock.cpp + * @brief A Bedrock Implementation * @contributors: Ariel Vina-Rodriguez */ @@ -17,7 +18,6 @@ #include #include #include -#include #include #include #include @@ -26,6 +26,8 @@ #include #include +#include //use std::cerr + #ifndef WM_MOUSEWHEEL #define WM_MOUSEWHEEL 0x020A #endif @@ -178,26 +180,6 @@ namespace detail thread_context *object{ nullptr }; }tcontext; }cache; - - struct menu_tag - { - 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; - - struct keyboard_tracking_state_tag - { - keyboard_tracking_state_tag() - :alt(0) - {} - - bool has_shortkey_occured = false; - bool has_keyup = true; - unsigned long alt : 2; - }keyboard_tracking_state; }; //class bedrock defines a static object itself to implement a static singleton @@ -252,17 +234,17 @@ namespace detail { if(wd_manager().number_of_core_window()) { - std::stringstream ss; - ss<<"Nana.GUI detects a memory leaks in window_manager, "<(wd_manager().number_of_core_window())<<" window(s) are not uninstalled."; - ::MessageBoxA(0, ss.str().c_str(), ("Nana C++ Library"), MB_OK); + std::string msg = "Nana.GUI detects a memory leaks in window_manager, " + std::to_string(wd_manager().number_of_core_window()) + " window(s) are not uninstalled."; + std::cerr << msg; /// \todo add list of cations of open windows and if aut testin GUI do auto Ok after 2 sec. + ::MessageBoxA(0, msg.c_str(), ("Nana C++ Library"), MB_OK); } delete impl_; delete pi_data_; } - //inc_window - //@brief: increament the number of windows + + /// @brief increament the number of windows in the thread id int bedrock::inc_window(unsigned tid) { //impl refers to the object of private_impl, the object is created when bedrock is creating. @@ -327,14 +309,28 @@ namespace detail return bedrock_object; } - void bedrock::map_thread_root_buffer(core_window_t* wd, bool forced, const rectangle* update_area) + void bedrock::flush_surface(core_window_t* wd, bool forced, const rectangle* update_area) { - auto stru = reinterpret_cast(::HeapAlloc(::GetProcessHeap(), 0, sizeof(detail::messages::map_thread))); - if (stru) + if (nana::system::this_thread_id() != wd->thread_id) { - if (FALSE == ::PostMessage(reinterpret_cast(wd->root), nana::detail::messages::map_thread_root_buffer, reinterpret_cast(wd), reinterpret_cast(stru))) - ::HeapFree(::GetProcessHeap(), 0, stru); + auto stru = reinterpret_cast(::HeapAlloc(::GetProcessHeap(), 0, sizeof(detail::messages::map_thread))); + if (stru) + { + stru->forced = forced; + stru->ignore_update_area = true; + + if (update_area) + { + stru->ignore_update_area = false; + stru->update_area = *update_area; + } + + if (FALSE == ::PostMessage(reinterpret_cast(wd->root), nana::detail::messages::remote_flush_surface, reinterpret_cast(wd), reinterpret_cast(stru))) + ::HeapFree(::GetProcessHeap(), 0, stru); + } } + else + wd->drawer.map(reinterpret_cast(wd), forced, update_area); } void interior_helper_for_menu(MSG& msg, native_window_type menu_window) @@ -461,6 +457,7 @@ namespace detail { (msgbox(modal_window, "An exception during message pumping!").icon(msgbox::icon_information) <<"An uncaptured non-std exception during message pumping!" + << "\n in form: " << API::window_caption(modal_window) ).show(); internal_scope_guard lock; _m_except_handler(); @@ -546,13 +543,6 @@ namespace detail } } - void assign_arg(arg_focus& arg, basic_window* wd, native_window_type recv, bool getting) - { - arg.window_handle = reinterpret_cast(wd); - arg.receiver = recv; - arg.getting = getting; - } - void assign_arg(arg_wheel& arg, basic_window* wd, const parameter_decoder& pmdec) { arg.window_handle = reinterpret_cast(wd); @@ -603,7 +593,7 @@ namespace detail break; } return true; - case nana::detail::messages::map_thread_root_buffer: + case nana::detail::messages::remote_flush_surface: { auto stru = reinterpret_cast(lParam); bedrock.wd_manager().map(reinterpret_cast(wParam), stru->forced, (stru->ignore_update_area ? nullptr : &stru->update_area)); @@ -665,6 +655,14 @@ namespace detail case nana::detail::messages::tray: notifications_window_proc(wd, wParam, lParam); return true; + case nana::detail::messages::affinity_execute: + if (wParam) + { + auto arg = reinterpret_cast(wParam); + if (arg->function_ptr) + (*arg->function_ptr)(); + } + break; default: break; } @@ -757,7 +755,7 @@ namespace detail } template - void emit_drawer(void (::nana::detail::drawer::*event_ptr)(const Arg&), basic_window* wd, const Arg& arg, bedrock::thread_context* thrd) + void draw_invoker(void (::nana::detail::drawer::*event_ptr)(const Arg&), basic_window* wd, const Arg& arg, bedrock::thread_context* thrd) { if (bedrock::instance().wd_manager().available(wd) == false) return; @@ -787,7 +785,9 @@ namespace detail static restrict::TRACKMOUSEEVENT track = {sizeof track, 0x00000002}; auto native_window = reinterpret_cast(root_window); - auto* root_runtime = brock.wd_manager().root_runtime(native_window); + + auto & wd_manager = brock.wd_manager(); + auto* root_runtime = wd_manager.root_runtime(native_window); if(root_runtime) { @@ -832,7 +832,7 @@ namespace detail bool take_over = false; auto mmi = reinterpret_cast(lParam); - if(msgwnd->min_track_size.width && msgwnd->min_track_size.height) + if(!msgwnd->min_track_size.empty()) { mmi->ptMinTrackSize.x = static_cast(msgwnd->min_track_size.width + msgwnd->extra_width); mmi->ptMinTrackSize.y = static_cast(msgwnd->min_track_size.height + msgwnd->extra_height); @@ -859,10 +859,9 @@ namespace detail } break; case WM_SHOWWINDOW: - if (msgwnd->visible && (wParam == FALSE)) - brock.event_expose(msgwnd, false); - else if ((!msgwnd->visible) && (wParam != FALSE)) - brock.event_expose(msgwnd, true); + if (msgwnd->visible == (FALSE == wParam)) + brock.event_expose(msgwnd, !msgwnd->visible); + def_window_proc = true; break; case WM_WINDOWPOSCHANGED: @@ -874,37 +873,12 @@ namespace detail def_window_proc = true; break; case WM_SETFOCUS: - if(msgwnd->flags.enabled && msgwnd->flags.take_active) - { - auto focus = msgwnd->other.attribute.root->focus; - - if(focus && focus->together.caret) - focus->together.caret->set_active(true); - - arg_focus arg; - assign_arg(arg, focus, native_window, true); - if (!brock.emit(event_code::focus, focus, arg, true, &context)) - brock.wd_manager().set_focus(msgwnd, true); - } + brock.event_focus_changed(msgwnd, native_window, true); def_window_proc = true; break; case WM_KILLFOCUS: - if(msgwnd->other.attribute.root->focus) - { - auto focus = msgwnd->other.attribute.root->focus; - - arg_focus arg; - assign_arg(arg, focus, reinterpret_cast(wParam), false); - if(brock.emit(event_code::focus, focus, arg, true, &context)) - { - if(focus->together.caret) - focus->together.caret->set_active(false); - } - - //wParam indicates a handle of window that receives the focus. - brock.close_menu_if_focus_other_window(reinterpret_cast(wParam)); - } - + //wParam indicates a handle of window that receives the focus. + brock.event_focus_changed(msgwnd, reinterpret_cast(wParam), false); def_window_proc = true; break; case WM_MOUSEACTIVATE: @@ -919,23 +893,20 @@ namespace detail break; pressed_wd = nullptr; - msgwnd = brock.wd_manager().find_window(native_window, pmdec.mouse.x, pmdec.mouse.y); + msgwnd = wd_manager.find_window(native_window, pmdec.mouse.x, pmdec.mouse.y); if(msgwnd && msgwnd->flags.enabled) { if (msgwnd->flags.take_active && !msgwnd->flags.ignore_mouse_focus) { - auto killed = brock.wd_manager().set_focus(msgwnd, false); + auto killed = brock.wd_manager().set_focus(msgwnd, false, arg_focus::reason::mouse_press); if (killed != msgwnd) - brock.wd_manager().do_lazy_refresh(killed, false); + wd_manager.do_lazy_refresh(killed, false); } arg_mouse arg; assign_arg(arg, msgwnd, message, pmdec); if (brock.emit(arg.evt_code, msgwnd, arg, true, &context)) - { - if (brock.wd_manager().available(msgwnd)) - pressed_wd = msgwnd; - } + pressed_wd = msgwnd; } break; case WM_NCLBUTTONDOWN: case WM_NCMBUTTONDOWN: case WM_NCRBUTTONDOWN: @@ -947,7 +918,7 @@ namespace detail if (pressed_wd_space) break; - msgwnd = brock.wd_manager().find_window(native_window, pmdec.mouse.x, pmdec.mouse.y); + msgwnd = wd_manager.find_window(native_window, pmdec.mouse.x, pmdec.mouse.y); if ((nullptr == msgwnd) || (pressed_wd && (msgwnd != pressed_wd))) break; @@ -966,17 +937,17 @@ namespace detail auto new_focus = (msgwnd->flags.take_active ? msgwnd : msgwnd->other.active_window); if (new_focus && (!new_focus->flags.ignore_mouse_focus)) { - auto kill_focus = brock.wd_manager().set_focus(new_focus, false); + auto kill_focus = brock.wd_manager().set_focus(new_focus, false, arg_focus::reason::mouse_press); if (kill_focus != new_focus) - brock.wd_manager().do_lazy_refresh(kill_focus, false); + wd_manager.do_lazy_refresh(kill_focus, false); } } arg_mouse arg; assign_arg(arg, msgwnd, message, pmdec); - msgwnd->flags.action = mouse_action::pressed; + msgwnd->set_action(mouse_action::pressed); - auto retain = msgwnd->together.events_ptr; + auto retain = msgwnd->annex.events_ptr; if (brock.emit(event_code::mouse_down, msgwnd, arg, true, &context)) { //If a root_window is created during the mouse_down event, Nana.GUI will ignore the mouse_up event. @@ -985,14 +956,14 @@ namespace detail auto pos = native_interface::cursor_position(); auto rootwd = native_interface::find_window(pos.x, pos.y); native_interface::calc_window_point(rootwd, pos); - if(msgwnd != brock.wd_manager().find_window(rootwd, pos.x, pos.y)) + if(msgwnd != wd_manager.find_window(rootwd, pos.x, pos.y)) { //call the drawer mouse up event for restoring the surface graphics - msgwnd->flags.action = mouse_action::normal; + msgwnd->set_action(mouse_action::normal); arg.evt_code = event_code::mouse_up; - emit_drawer(&drawer::mouse_up, msgwnd, arg, &context); - brock.wd_manager().do_lazy_refresh(msgwnd, false); + draw_invoker(&drawer::mouse_up, msgwnd, arg, &context); + wd_manager.do_lazy_refresh(msgwnd, false); } } } @@ -1008,14 +979,14 @@ namespace detail if (pressed_wd_space) break; - msgwnd = brock.wd_manager().find_window(native_window, pmdec.mouse.x, pmdec.mouse.y); + msgwnd = wd_manager.find_window(native_window, pmdec.mouse.x, pmdec.mouse.y); if(nullptr == msgwnd) break; - msgwnd->flags.action = mouse_action::normal; + msgwnd->set_action(mouse_action::normal); if(msgwnd->flags.enabled) { - auto retain = msgwnd->together.events_ptr; + auto retain = msgwnd->annex.events_ptr; ::nana::arg_mouse arg; assign_arg(arg, msgwnd, message, pmdec); @@ -1028,33 +999,33 @@ namespace detail if (msgwnd->dimension.is_hit(arg.pos)) { - msgwnd->flags.action = mouse_action::over; + msgwnd->set_action(mouse_action::hovered); if ((::nana::mouse::left_button == arg.button) && (pressed_wd == msgwnd)) { click_arg.window_handle = reinterpret_cast(msgwnd); - emit_drawer(&drawer::click, msgwnd, click_arg, &context); + draw_invoker(&drawer::click, msgwnd, click_arg, &context); } } //Do mouse_up, this handle may be closed by click handler. - if(brock.wd_manager().available(msgwnd) && msgwnd->flags.enabled) + if(wd_manager.available(msgwnd) && msgwnd->flags.enabled) { arg.evt_code = event_code::mouse_up; - emit_drawer(&drawer::mouse_up, msgwnd, arg, &context); + draw_invoker(&drawer::mouse_up, msgwnd, arg, &context); if (click_arg.window_handle) - retain->click.emit(click_arg); + retain->click.emit(click_arg, reinterpret_cast(msgwnd)); - if (brock.wd_manager().available(msgwnd)) + if (wd_manager.available(msgwnd)) { arg.evt_code = event_code::mouse_up; - retain->mouse_up.emit(arg); + retain->mouse_up.emit(arg, reinterpret_cast(msgwnd)); } } else if (click_arg.window_handle) - retain->click.emit(click_arg); + retain->click.emit(click_arg, reinterpret_cast(msgwnd)); - brock.wd_manager().do_lazy_refresh(msgwnd, false); + wd_manager.do_lazy_refresh(msgwnd, false); } pressed_wd = nullptr; break; @@ -1063,16 +1034,16 @@ namespace detail if (pressed_wd_space) break; - msgwnd = brock.wd_manager().find_window(native_window, pmdec.mouse.x, pmdec.mouse.y); - if (brock.wd_manager().available(hovered_wd) && (msgwnd != hovered_wd)) + msgwnd = wd_manager.find_window(native_window, pmdec.mouse.x, pmdec.mouse.y); + if (wd_manager.available(hovered_wd) && (msgwnd != hovered_wd)) { brock.event_msleave(hovered_wd); - hovered_wd->flags.action = mouse_action::normal; + hovered_wd->set_action(mouse_action::normal); hovered_wd = nullptr; //if msgwnd is neither captured window nor the child of captured window, //redirect the msgwnd to the captured window. - auto wd = brock.wd_manager().capture_redirect(msgwnd); + auto wd = wd_manager.capture_redirect(msgwnd); if(wd) msgwnd = wd; } @@ -1080,21 +1051,21 @@ namespace detail else if(msgwnd) { bool prev_captured_inside; - if(brock.wd_manager().capture_window_entered(pmdec.mouse.x, pmdec.mouse.y, prev_captured_inside)) + if(wd_manager.capture_window_entered(pmdec.mouse.x, pmdec.mouse.y, prev_captured_inside)) { event_code evt_code; if(prev_captured_inside) { evt_code = event_code::mouse_leave; - msgwnd->flags.action = mouse_action::normal; + msgwnd->set_action(mouse_action::normal); } else { evt_code = event_code::mouse_enter; if (pressed_wd == msgwnd) - msgwnd->flags.action = mouse_action::pressed; + msgwnd->set_action(mouse_action::pressed); else if (mouse_action::pressed != msgwnd->flags.action) - msgwnd->flags.action = mouse_action::over; + msgwnd->set_action(mouse_action::hovered); } arg_mouse arg; assign_arg(arg, msgwnd, message, pmdec); @@ -1111,21 +1082,24 @@ namespace detail if (hovered_wd != msgwnd) { if (pressed_wd == msgwnd) - msgwnd->flags.action = mouse_action::pressed; + msgwnd->set_action(mouse_action::pressed); else if (mouse_action::pressed != msgwnd->flags.action) - msgwnd->flags.action = mouse_action::over; + msgwnd->set_action(mouse_action::hovered); hovered_wd = msgwnd; arg.evt_code = event_code::mouse_enter; brock.emit(event_code::mouse_enter, msgwnd, arg, true, &context); } - arg.evt_code = event_code::mouse_move; - brock.emit(event_code::mouse_move, msgwnd, arg, true, &context); + if (hovered_wd) + { + arg.evt_code = event_code::mouse_move; + brock.emit(event_code::mouse_move, msgwnd, arg, true, &context); + } track.hwndTrack = native_window; restrict::track_mouse_event(&track); } - if (!brock.wd_manager().available(hovered_wd)) + if (!wd_manager.available(hovered_wd)) hovered_wd = nullptr; break; case WM_MOUSELEAVE: @@ -1141,17 +1115,17 @@ namespace detail if (pointer_wd == root_window) { ::ScreenToClient(pointer_wd, &scr_pos); - auto scrolled_wd = brock.wd_manager().find_window(reinterpret_cast(pointer_wd), scr_pos.x, scr_pos.y); + auto scrolled_wd = wd_manager.find_window(reinterpret_cast(pointer_wd), scr_pos.x, scr_pos.y); def_window_proc = true; auto evt_wd = scrolled_wd; while (evt_wd) { - if (evt_wd->together.events_ptr->mouse_wheel.length() != 0) + if (evt_wd->annex.events_ptr->mouse_wheel.length() != 0) { def_window_proc = false; nana::point mspos{ scr_pos.x, scr_pos.y }; - brock.wd_manager().calc_window_point(evt_wd, mspos); + wd_manager.calc_window_point(evt_wd, mspos); arg_wheel arg; arg.which = (WM_MOUSEHWHEEL == message ? arg_wheel::wheel::horizontal : arg_wheel::wheel::vertical); @@ -1165,13 +1139,14 @@ namespace detail if (scrolled_wd && (nullptr == evt_wd)) { nana::point mspos{ scr_pos.x, scr_pos.y }; - brock.wd_manager().calc_window_point(scrolled_wd, mspos); + wd_manager.calc_window_point(scrolled_wd, mspos); arg_wheel arg; arg.which = (WM_MOUSEHWHEEL == message ? arg_wheel::wheel::horizontal : arg_wheel::wheel::vertical); assign_arg(arg, scrolled_wd, pmdec); - brock.emit_drawer(event_code::mouse_wheel, scrolled_wd, arg, &context); - brock.wd_manager().do_lazy_refresh(scrolled_wd, false); + + draw_invoker(&drawer::mouse_wheel, scrolled_wd, arg, &context); + wd_manager.do_lazy_refresh(scrolled_wd, false); } } else @@ -1189,7 +1164,7 @@ namespace detail POINT pos; ::DragQueryPoint(drop, &pos); - msgwnd = brock.wd_manager().find_window(native_window, pos.x, pos.y); + msgwnd = wd_manager.find_window(native_window, pos.x, pos.y); if(msgwnd) { arg_dropfiles dropfiles; @@ -1220,11 +1195,11 @@ namespace detail dropfiles.pos.x = pos.x; dropfiles.pos.y = pos.y; - brock.wd_manager().calc_window_point(msgwnd, dropfiles.pos); + wd_manager.calc_window_point(msgwnd, dropfiles.pos); dropfiles.window_handle = reinterpret_cast(msgwnd); - msgwnd->together.events_ptr->mouse_dropfiles.emit(dropfiles); - brock.wd_manager().do_lazy_refresh(msgwnd, false); + msgwnd->annex.events_ptr->mouse_dropfiles.emit(dropfiles, reinterpret_cast(msgwnd)); + wd_manager.do_lazy_refresh(msgwnd, false); } } @@ -1312,7 +1287,7 @@ namespace detail break; case WM_SIZE: if(wParam != SIZE_MINIMIZED) - brock.wd_manager().size(msgwnd, size(pmdec.size.width, pmdec.size.height), true, true); + wd_manager.size(msgwnd, size(pmdec.size.width, pmdec.size.height), true, true); break; case WM_MOVE: brock.event_move(msgwnd, (int)(short) LOWORD(lParam), (int)(short) HIWORD(lParam)); @@ -1337,8 +1312,8 @@ namespace detail break; case WM_SYSCHAR: def_window_proc = true; - brock.set_keyboard_shortkey(true); - msgwnd = brock.wd_manager().find_shortkey(native_window, static_cast(wParam)); + brock.shortkey_occurred(true); + msgwnd = wd_manager.find_shortkey(native_window, static_cast(wParam)); if(msgwnd) { arg_keyboard arg; @@ -1353,7 +1328,7 @@ namespace detail break; case WM_SYSKEYDOWN: def_window_proc = true; - if (brock.whether_keyboard_shortkey() == false) + if (brock.shortkey_occurred() == false) { msgwnd = msgwnd->root_widget->other.attribute.root->menubar; if (msgwnd) @@ -1375,7 +1350,7 @@ namespace detail break; case WM_SYSKEYUP: def_window_proc = true; - if(brock.set_keyboard_shortkey(false) == false) + if (brock.shortkey_occurred(false) == false) { msgwnd = msgwnd->root_widget->other.attribute.root->menubar; if(msgwnd) @@ -1385,7 +1360,7 @@ namespace detail bool set_focus = (brock.focus() != msgwnd) && (!msgwnd->root_widget->flags.ignore_menubar_focus); if (set_focus) - brock.wd_manager().set_focus(msgwnd, false); + wd_manager.set_focus(msgwnd, false, arg_focus::reason::general); arg_keyboard arg; arg.evt_code = event_code::key_release; @@ -1415,7 +1390,6 @@ namespace detail if(msgwnd) { - auto & wd_manager = brock.wd_manager(); if((VK_TAB == wParam) && (!msgwnd->visible || (false == (msgwnd->flags.tab & tab_type::eating)))) //Tab { bool is_forward = (::GetKeyState(VK_SHIFT) >= 0); @@ -1423,7 +1397,8 @@ namespace detail auto tstop_wd = wd_manager.tabstop(msgwnd, is_forward); if (tstop_wd) { - wd_manager.set_focus(tstop_wd, false); + root_runtime->condition.ignore_tab = true; + wd_manager.set_focus(tstop_wd, false, arg_focus::reason::tabstop); wd_manager.do_lazy_refresh(msgwnd, false); wd_manager.do_lazy_refresh(tstop_wd, true); } @@ -1444,12 +1419,12 @@ namespace detail arg.pos.y = 0; arg.window_handle = reinterpret_cast(msgwnd); - msgwnd->flags.action = mouse_action::pressed; + msgwnd->set_action(mouse_action::pressed); pressed_wd_space = msgwnd; - auto retain = msgwnd->together.events_ptr; + auto retain = msgwnd->annex.events_ptr; - emit_drawer(&drawer::mouse_down, msgwnd, arg, &context); + draw_invoker(&drawer::mouse_down, msgwnd, arg, &context); wd_manager.do_lazy_refresh(msgwnd, false); } } @@ -1481,8 +1456,10 @@ namespace detail msgwnd = brock.focus(); if (msgwnd && msgwnd->flags.enabled) { - // When tab is pressed, only tab-eating mode is allowed - if ((9 != wParam) || (msgwnd->flags.tab & tab_type::eating)) + auto & wd_manager = brock.wd_manager(); + + //Only accept tab when it is not ignored. + if (VK_TAB != wParam || !root_runtime->condition.ignore_tab) { arg_keyboard arg; arg.evt_code = event_code::key_char; @@ -1491,61 +1468,68 @@ namespace detail brock.get_key_state(arg); arg.ignore = false; - msgwnd->together.events_ptr->key_char.emit(arg); - if ((false == arg.ignore) && brock.wd_manager().available(msgwnd)) - brock.emit_drawer(event_code::key_char, msgwnd, arg, &context); + msgwnd->annex.events_ptr->key_char.emit(arg, reinterpret_cast(msgwnd)); + if ((false == arg.ignore) && wd_manager.available(msgwnd)) + draw_invoker(&drawer::key_char, msgwnd, arg, &context); - brock.wd_manager().do_lazy_refresh(msgwnd, false); + wd_manager.do_lazy_refresh(msgwnd, false); } } return 0; case WM_KEYUP: if(wParam != VK_MENU) //MUST NOT BE AN ALT { - msgwnd = brock.focus(); - if(msgwnd) + if (VK_TAB == wParam && root_runtime->condition.ignore_tab) { - if (msgwnd == pressed_wd_space) + root_runtime->condition.ignore_tab = false; + } + else + { + msgwnd = brock.focus(); + if (msgwnd) { - msgwnd->flags.action = mouse_action::normal; - - arg_click click_arg; - click_arg.mouse_args = nullptr; - click_arg.window_handle = reinterpret_cast(msgwnd); - - auto retain = msgwnd->together.events_ptr; - if (brock.emit(event_code::click, msgwnd, click_arg, true, &context)) + if (msgwnd == pressed_wd_space) { - arg_mouse arg; - arg.alt = false; - arg.button = ::nana::mouse::left_button; - arg.ctrl = false; - arg.evt_code = event_code::mouse_up; - arg.left_button = true; - arg.mid_button = false; - arg.pos.x = 0; - arg.pos.y = 0; - arg.window_handle = reinterpret_cast(msgwnd); + msgwnd->set_action(mouse_action::normal); - emit_drawer(&drawer::mouse_up, msgwnd, arg, &context); - brock.wd_manager().do_lazy_refresh(msgwnd, false); + arg_click click_arg; + click_arg.mouse_args = nullptr; + click_arg.window_handle = reinterpret_cast(msgwnd); + + auto retain = msgwnd->annex.events_ptr; + if (brock.emit(event_code::click, msgwnd, click_arg, true, &context)) + { + arg_mouse arg; + arg.alt = false; + arg.button = ::nana::mouse::left_button; + arg.ctrl = false; + arg.evt_code = event_code::mouse_up; + arg.left_button = true; + arg.mid_button = false; + arg.pos.x = 0; + arg.pos.y = 0; + arg.window_handle = reinterpret_cast(msgwnd); + + draw_invoker(&drawer::mouse_up, msgwnd, arg, &context); + wd_manager.do_lazy_refresh(msgwnd, false); + } + pressed_wd_space = nullptr; + } + else + { + arg_keyboard arg; + arg.evt_code = event_code::key_release; + arg.window_handle = reinterpret_cast(msgwnd); + arg.key = static_cast(wParam); + brock.get_key_state(arg); + arg.ignore = false; + brock.emit(event_code::key_release, msgwnd, arg, true, &context); } - pressed_wd_space = nullptr; - } - else - { - arg_keyboard arg; - arg.evt_code = event_code::key_release; - arg.window_handle = reinterpret_cast(msgwnd); - arg.key = static_cast(wParam); - brock.get_key_state(arg); - arg.ignore = false; - brock.emit(event_code::key_release, msgwnd, arg, true, &context); } } } else - brock.set_keyboard_shortkey(false); + brock.shortkey_occurred(false); //Do delay restore if key is not arrow_left/right/up/down, otherwise //A menubar will be restored if the item is empty(not have a menu item) @@ -1573,12 +1557,12 @@ namespace detail brock.erase_menu(false); brock.delay_restore(3); //Restores if delay_restore not decleared } - brock.wd_manager().destroy(msgwnd); + wd_manager.destroy(msgwnd); nana::detail::platform_spec::instance().release_window_icon(msgwnd->root); break; case WM_NCDESTROY: brock.manage_form_loader(msgwnd, false); - brock.wd_manager().destroy_handle(msgwnd); + wd_manager.destroy_handle(msgwnd); if(--context.window_count <= 0) { @@ -1590,7 +1574,7 @@ namespace detail def_window_proc = true; } - root_runtime = brock.wd_manager().root_runtime(native_window); + root_runtime = wd_manager.root_runtime(native_window); if(root_runtime) { root_runtime->condition.pressed = pressed_wd; @@ -1605,132 +1589,18 @@ namespace detail return ::DefWindowProc(root_window, message, wParam, lParam); } - ::nana::category::flags bedrock::category(core_window_t* wd) - { - internal_scope_guard lock; - return (wd_manager().available(wd) ? wd->other.category : ::nana::category::flags::super); - } - 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); } - void bedrock::set_menubar_taken(core_window_t* wd) - { - auto pre = impl_->menu.taken_window; - impl_->menu.taken_window = wd; - - //assigning of a nullptr taken window is to restore the focus of pre taken - //don't restore the focus if pre is a menu. - if ((!wd) && pre && (pre->root != get_menu())) - { - internal_scope_guard lock; - wd_manager().set_focus(pre, false); - wd_manager().update(pre, true, false); - } - } - - //0:Enable delay, 1:Cancel, 2:Restores, 3: Restores when menu is destroying - void bedrock::delay_restore(int state) - { - switch (state) - { - case 0: //Enable - break; - case 1: //Cancel - break; - case 2: //Restore if key released - //restores the focus when menu is closed by pressing keyboard - if ((!impl_->menu.window) && impl_->menu.delay_restore) - set_menubar_taken(nullptr); - break; - case 3: //Restores if destroying - //when the menu is destroying, restores the focus if delay restore is not declared - if (!impl_->menu.delay_restore) - set_menubar_taken(nullptr); - } - - impl_->menu.delay_restore = (0 == state); - } - - bool bedrock::close_menu_if_focus_other_window(native_window_type wd) - { - if(impl_->menu.window && (impl_->menu.window != wd)) - { - wd = native_interface::get_owner_window(wd); - while(wd) - { - if(wd != impl_->menu.window) - wd = native_interface::get_owner_window(wd); - else - return false; - } - erase_menu(true); - return true; - } - return false; - } - - void bedrock::set_menu(native_window_type menu_wd, bool has_keyboard) - { - if(menu_wd && impl_->menu.window != menu_wd) - { - erase_menu(true); - - impl_->menu.window = menu_wd; - impl_->menu.owner = native_interface::get_owner_window(menu_wd); - impl_->menu.has_keyboard = has_keyboard; - } - } - - native_window_type bedrock::get_menu(native_window_type owner, bool is_keyboard_condition) - { - if( (impl_->menu.owner == nullptr) || - (owner && (impl_->menu.owner == owner)) - ) - { - return ((!is_keyboard_condition) || impl_->menu.has_keyboard ? impl_->menu.window : nullptr); - } - return nullptr; - } - - native_window_type bedrock::get_menu() - { - return impl_->menu.window; - } - - void bedrock::erase_menu(bool try_destroy) - { - if (impl_->menu.window) - { - if (try_destroy) - native_interface::close_window(impl_->menu.window); - - impl_->menu.window = impl_->menu.owner = nullptr; - impl_->menu.has_keyboard = false; - } - } - void bedrock::get_key_state(arg_keyboard& kb) { kb.ctrl = (0 != (::GetKeyState(VK_CONTROL) & 0x80)); kb.shift = (0 != (::GetKeyState(VK_SHIFT) & 0x80)); } - bool bedrock::set_keyboard_shortkey(bool yes) - { - bool ret = impl_->keyboard_tracking_state.has_shortkey_occured; - impl_->keyboard_tracking_state.has_shortkey_occured = yes; - return ret; - } - - bool bedrock::whether_keyboard_shortkey() const - { - return impl_->keyboard_tracking_state.has_shortkey_occured; - } - element_store& bedrock::get_element_store() const { return impl_->estore; @@ -1738,7 +1608,6 @@ namespace detail void bedrock::map_through_widgets(core_window_t* wd, native_drawable_type drawable) { -#if defined(NANA_WINDOWS) auto graph_context = reinterpret_cast(wd->root_graph->handle()->context); for (auto child : wd->children) @@ -1753,7 +1622,6 @@ namespace detail else if (::nana::category::flags::lite_widget == child->other.category) map_through_widgets(child, drawable); } -#endif } bool bedrock::emit(event_code evt_code, core_window_t* wd, const ::nana::event_arg& arg, bool ask_update, thread_context* thrd) @@ -1774,34 +1642,19 @@ namespace detail _m_emit_core(evt_code, wd, false, arg); - if (ask_update) - wd_manager().do_lazy_refresh(wd, false); - else if (wd_manager().available(wd)) - wd->other.upd_state = basic_window::update_state::none; - - if (thrd) thrd->event_window = prev_event_wd; - return true; - } - - bool bedrock::emit_drawer(event_code evt_code, core_window_t* wd, const ::nana::event_arg& arg, thread_context* thrd) - { - if (bedrock_object.wd_manager().available(wd) == false) - return false; - - core_window_t* prev_event_wd; - if (thrd) + bool good_wd = false; + if (wd_manager().available(wd)) { - prev_event_wd = thrd->event_window; - thrd->event_window = wd; + if (ask_update) + wd_manager().do_lazy_refresh(wd, false); + else + wd->other.upd_state = basic_window::update_state::none; + + good_wd = true; } - if (wd->other.upd_state == core_window_t::update_state::none) - wd->other.upd_state = core_window_t::update_state::lazy; - - _m_emit_core(evt_code, wd, true, arg); - - if (thrd) thrd->event_window = prev_event_wd; - return true; + if (thrd) thrd->event_window = prev_event_wd; + return good_wd; } const wchar_t* translate(cursor id) diff --git a/source/gui/detail/color_schemes.cpp b/source/gui/detail/color_schemes.cpp index c7fcd530..d9fca610 100644 --- a/source/gui/detail/color_schemes.cpp +++ b/source/gui/detail/color_schemes.cpp @@ -1,3 +1,15 @@ +/* +* Color Schemes +* Nana C++ Library(http://www.nanapro.org) +* Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) +* +* Distributed under the Boost Software License, Version 1.0. +* (See accompanying file LICENSE_1_0.txt or copy at +* http://www.boost.org/LICENSE_1_0.txt) +* +* @file: nana/gui/color_schemes.cpp +*/ + #include #include @@ -57,7 +69,7 @@ namespace nana //class color_schemes struct color_schemes::implement { - std::map> scheme_template; + std::map> scheme_template; }; color_schemes::color_schemes() @@ -70,7 +82,7 @@ namespace nana delete impl_; } - auto color_schemes::scheme_template(scheme_factory_base&& factory) -> scheme& + auto color_schemes::scheme_template(scheme_factory_interface&& factory) -> scheme& { auto & tmpl_scheme = impl_->scheme_template[factory.get_id()]; @@ -81,7 +93,7 @@ namespace nana return *tmpl_scheme.get(); } - widget_colors* color_schemes::create(scheme_factory_base&& factory) + widget_geometrics* color_schemes::create(scheme_factory_interface&& factory) { return factory.create(scheme_template(std::move(factory))); } diff --git a/source/gui/detail/drawer.cpp b/source/gui/detail/drawer.cpp index 1cce7648..fdf569aa 100644 --- a/source/gui/detail/drawer.cpp +++ b/source/gui/detail/drawer.cpp @@ -1,7 +1,7 @@ /* * A Drawer Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2014 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -26,7 +26,6 @@ namespace nana typedef detail::edge_nimbus_renderer edge_nimbus_renderer_t; //class drawer_trigger - drawer_trigger::~drawer_trigger(){} void drawer_trigger::attached(widget_reference, graph_reference){} void drawer_trigger::detached(){} //none-const void drawer_trigger::typeface_changed(graph_reference){} @@ -136,23 +135,43 @@ namespace nana typedef bedrock bedrock_type; //class drawer + + enum{ + event_size = static_cast(event_code::end) + }; + + struct drawer::data_implement + { + bool refreshing{ false }; + basic_window* window_handle{ nullptr }; + drawer_trigger* realizer{ nullptr }; + method_state mth_state[event_size]; + std::vector draws; + }; + + drawer::drawer() + : data_impl_{ new data_implement } + {} + drawer::~drawer() { - for(auto p : dynamic_drawing_objects_) + for(auto p : data_impl_->draws) { delete p; } + + delete data_impl_; } void drawer::bind(basic_window* cw) { - core_window_ = cw; + data_impl_->window_handle = cw; } void drawer::typeface_changed() { - if(realizer_) - realizer_->typeface_changed(graphics); + if(data_impl_->realizer) + data_impl_->realizer->typeface_changed(graphics); } void drawer::click(const arg_click& arg) @@ -245,9 +264,7 @@ namespace nana if(wd) { auto iwd = reinterpret_cast(wd); - auto caret_wd = iwd->root_widget->other.attribute.root->focus; - - bool owns_caret = (caret_wd && (caret_wd->together.caret) && (caret_wd->together.caret->visible())); + bool owns_caret = (iwd->annex.caret_ptr) && (iwd->annex.caret_ptr->visible()); //The caret in X11 is implemented by Nana, it is different from Windows' //the caret in X11 is asynchronous, it is hard to hide and show the caret @@ -256,7 +273,7 @@ namespace nana if(owns_caret) { #ifndef NANA_X11 - caret_wd->together.caret->visible(false); + iwd->annex.caret_ptr->visible(false); #else owns_caret = nana::detail::platform_spec::instance().caret_update(iwd->root, *iwd->root_graph, false); #endif @@ -267,7 +284,7 @@ namespace nana if(owns_caret) { #ifndef NANA_X11 - caret_wd->together.caret->visible(true); + iwd->annex.caret_ptr->visible(true); #else nana::detail::platform_spec::instance().caret_update(iwd->root, *iwd->root_graph, true); #endif @@ -277,39 +294,38 @@ namespace nana void drawer::refresh() { - if(realizer_ && (refreshing_ == false)) + if (data_impl_->realizer && !data_impl_->refreshing) { - refreshing_ = true; - _m_bground_pre(); - realizer_->refresh(graphics); - _m_draw_dynamic_drawing_object(); - _m_bground_end(); + data_impl_->refreshing = true; + _m_effect_bground(true); + data_impl_->realizer->refresh(graphics); + _m_effect_bground(false); graphics.flush(); - refreshing_ = false; + data_impl_->refreshing = false; } } drawer_trigger* drawer::realizer() const { - return realizer_; + return data_impl_->realizer; } void drawer::attached(widget& wd, drawer_trigger& realizer) { - for (auto i = std::begin(mth_state_), end = std::end(mth_state_); i != end; ++i) + for (auto i = std::begin(data_impl_->mth_state), end = std::end(data_impl_->mth_state); i != end; ++i) *i = method_state::pending; - realizer_ = &realizer; + data_impl_->realizer = &realizer; realizer._m_reset_overrided(); realizer.attached(wd, graphics); } drawer_trigger* drawer::detached() { - if(realizer_) + if (data_impl_->realizer) { - auto rmp = realizer_; - realizer_ = nullptr; + auto rmp = data_impl_->realizer; + data_impl_->realizer = nullptr; rmp->detached(); return rmp; } @@ -319,7 +335,7 @@ namespace nana void drawer::clear() { std::vector then; - for(auto p : dynamic_drawing_objects_) + for (auto p : data_impl_->draws) { if(p->diehard()) then.push_back(p); @@ -327,7 +343,7 @@ namespace nana delete p; } - then.swap(dynamic_drawing_objects_); + then.swap(data_impl_->draws); } void* drawer::draw(std::function && f, bool diehard) @@ -335,7 +351,7 @@ namespace nana if(f) { auto p = new dynamic_drawing::user_draw_function(std::move(f), diehard); - dynamic_drawing_objects_.push_back(p); + data_impl_->draws.push_back(p); return (diehard ? p : nullptr); } return nullptr; @@ -345,37 +361,42 @@ namespace nana { if(p) { - for (auto i = dynamic_drawing_objects_.begin(); i != dynamic_drawing_objects_.end(); ++i) + for (auto i = data_impl_->draws.begin(); i != data_impl_->draws.end(); ++i) if (*i == p) { delete (*i); - dynamic_drawing_objects_.erase(i); + data_impl_->draws.erase(i); break; } } } - void drawer::_m_bground_pre() + void drawer::_m_effect_bground(bool before) { - if(core_window_->effect.bground && core_window_->effect.bground_fade_rate < 0.01) - core_window_->other.glass_buffer.paste(graphics, 0, 0); - } - - void drawer::_m_bground_end() - { - if(core_window_->effect.bground && core_window_->effect.bground_fade_rate >= 0.01) - core_window_->other.glass_buffer.blend(::nana::rectangle{ core_window_->other.glass_buffer.size() }, graphics, nana::point(), core_window_->effect.bground_fade_rate); - } - - void drawer::_m_draw_dynamic_drawing_object() - { - for(auto * dw : dynamic_drawing_objects_) + for (auto * dw : data_impl_->draws) dw->draw(graphics); + + auto & effect = data_impl_->window_handle->effect; + if (effect.bground) + { + if (before) + { + if (effect.bground_fade_rate < 0.01) + data_impl_->window_handle->other.glass_buffer.paste(graphics, 0, 0); + } + else if (effect.bground_fade_rate >= 0.01) + data_impl_->window_handle->other.glass_buffer.blend(::nana::rectangle{ data_impl_->window_handle->other.glass_buffer.size() }, graphics, nana::point(), effect.bground_fade_rate); + } } bool drawer::_m_lazy_decleared() const { - return (basic_window::update_state::refresh == core_window_->other.upd_state); + return (basic_window::update_state::refresh == data_impl_->window_handle->other.upd_state); + } + + drawer::method_state& drawer::_m_mth_state(int pos) + { + return data_impl_->mth_state[pos]; } }//end namespace detail }//end namespace nana diff --git a/source/gui/detail/events_operation.cpp b/source/gui/detail/events_operation.cpp index 8877ebd1..bcafd688 100644 --- a/source/gui/detail/events_operation.cpp +++ b/source/gui/detail/events_operation.cpp @@ -1,4 +1,5 @@ #include +#include namespace nana { @@ -7,18 +8,6 @@ namespace nana //class events_operation using lock_guard = std::lock_guard; - void events_operation::make(window wd, const std::shared_ptr& sp) - { - lock_guard lock(mutex_); - evt_table_[wd] = sp; - } - - void events_operation::umake(window wd) - { - lock_guard lock(mutex_); - evt_table_.erase(wd); - } - void events_operation::register_evt(event_handle evt) { lock_guard lock(mutex_); @@ -42,5 +31,114 @@ namespace nana } } //end namespace events_operation + + + //class docker_base + docker_base::docker_base(event_interface* evt, bool unignorable_flag) + : event_ptr(evt), unignorable(unignorable_flag) + {} + + detail::event_interface * docker_base::get_event() const + { + return event_ptr; + } + //end class docker_base + + //class event_base + event_base::~event_base() + { + clear(); + } + + std::size_t event_base::length() const + { + internal_scope_guard lock; + return (nullptr == dockers_ ? 0 : dockers_->size()); + } + + void event_base::clear() noexcept + { + internal_scope_guard lock; + if (dockers_) + { + auto & evt_operation = bedrock::instance().evt_operation(); + + for (auto p : *dockers_) + { + evt_operation.cancel(reinterpret_cast(p)); + delete p; + } + dockers_->clear(); + + delete dockers_; + dockers_ = nullptr; + } + } + + void event_base::remove(event_handle evt) + { + internal_scope_guard lock; + if (dockers_) + { + + for (auto i = dockers_->begin(), end = dockers_->end(); i != end; ++i) + { + if (reinterpret_cast(evt) == *i) + { + //Checks whether this event is working now. + if (emitting_count_ > 1) + { + static_cast(*i)->flag_deleted = true; + deleted_flags_ = true; + } + else + dockers_->erase(i); + break; + } + } + } + } + + event_handle event_base::_m_emplace(detail::docker_interface* docker_ptr, bool in_front) + { + internal_scope_guard lock; + if (nullptr == dockers_) + dockers_ = new std::vector; + + auto evt = reinterpret_cast(docker_ptr); + + if (in_front) + dockers_->emplace(dockers_->begin(), docker_ptr); + else + dockers_->emplace_back(docker_ptr); + + detail::events_operation_register(evt); + return evt; + } + + //class emit_counter + event_base::emit_counter::emit_counter(event_base* evt) + : evt_{ evt } + { + ++evt->emitting_count_; + } + + event_base::emit_counter::~emit_counter() + { + if ((0 == --evt_->emitting_count_) && evt_->deleted_flags_) + { + evt_->deleted_flags_ = false; + for (auto i = evt_->dockers_->begin(); i != evt_->dockers_->end();) + { + if (static_cast(*i)->flag_deleted) + i = evt_->dockers_->erase(i); + else + ++i; + } + } + } + //end class emit_counter + //end class event_base + }//end namespace detail }//end namespace nana \ No newline at end of file diff --git a/source/gui/detail/native_window_interface.cpp b/source/gui/detail/native_window_interface.cpp index ea84c1d3..0657dd14 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-2015 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -13,6 +13,9 @@ #include #include #include +#include +#include + #if defined(NANA_WINDOWS) #if defined(STD_THREAD_NOT_SUPPORTED) #include @@ -23,28 +26,11 @@ #include "../../paint/detail/image_ico.hpp" #elif defined(NANA_X11) #include - #include - #include #endif +#include "../../paint/image_accessor.hpp" + namespace nana{ - namespace paint - { - class image_accessor - { - public: -#if defined(NANA_WINDOWS) - static HICON icon(const nana::paint::image& img) - { - auto ico = dynamic_cast(img.image_ptr_.get()); - if(ico && ico->ptr()) - return *(ico->ptr()); - return nullptr; - } -#endif - }; - } - namespace detail{ #if defined(NANA_WINDOWS) @@ -131,9 +117,13 @@ namespace nana{ } } if (async) + { ::ShowWindowAsync(wd, cmd); - else - ::ShowWindow(wd, cmd); + return; + } + + internal_revert_guard revert; + ::ShowWindow(wd, cmd); } #elif defined(NANA_X11) namespace restrict @@ -143,6 +133,34 @@ namespace nana{ #endif //struct native_interface + void native_interface::affinity_execute(native_window_type native_handle, const std::function& fn) + { + if (!fn) + return; + +#if defined(NANA_WINDOWS) + auto mswin = reinterpret_cast(native_handle); + if (::IsWindow(mswin)) + { + if (::GetCurrentThreadId() != ::GetWindowThreadProcessId(mswin, nullptr)) + { + detail::messages::arg_affinity_execute arg; + arg.function_ptr = &fn; + + internal_revert_guard revert; + ::SendMessage(mswin, detail::messages::affinity_execute, reinterpret_cast(&arg), 0); + + return; + } + } + + fn(); +#else + static_cast(native_handle); + fn(); +#endif + } + nana::size native_interface::primary_monitor_size() { #if defined(NANA_WINDOWS) @@ -173,6 +191,8 @@ namespace nana{ mi.rcWork.right - mi.rcWork.left, mi.rcWork.bottom - mi.rcWork.top); } } +#else + static_cast(pos); //eliminate unused parameter compiler warning. #endif return rectangle{ primary_monitor_size() }; } @@ -547,6 +567,8 @@ namespace nana{ activate_window(reinterpret_cast( ::GetWindow(reinterpret_cast(wd), GW_OWNER) )); +#else + static_cast(wd); //eliminate unused parameter compiler warning. #endif } @@ -565,6 +587,8 @@ namespace nana{ else ::PostMessage(native_wd, nana::detail::messages::async_activate, 0, 0); } +#else + static_cast(wd); //eliminate unused parameter compiler warning. #endif } @@ -640,6 +664,7 @@ namespace nana{ ::XFlush(disp); } + static_cast(active); //eliminate unused parameter compiler warning. #endif } @@ -715,6 +740,7 @@ namespace nana{ #if defined(NANA_WINDOWS) ::InvalidateRect(reinterpret_cast(wd), nullptr, true); #elif defined(NANA_X11) + static_cast(wd); //eliminate unused parameter compiler warning. #endif } @@ -963,6 +989,7 @@ namespace nana{ ::SetWindowPos(native_wd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); ::AttachThreadInput(::GetCurrentThreadId(), fg_tid, FALSE); #else + static_cast(activated); //eliminate unused parameter compiler warning. set_window_z_order(wd, nullptr, z_order_action::top); #endif } @@ -1110,11 +1137,18 @@ namespace nana{ auto native_interface::window_caption(native_window_type wd) -> native_string_type { + native_string_type str; + #if defined(NANA_WINDOWS) + auto & lock = bedrock::instance().wd_manager().internal_lock(); + bool is_current_thread = (::GetCurrentThreadId() == ::GetWindowThreadProcessId(reinterpret_cast(wd), nullptr)); + + if (!is_current_thread) + lock.revert(); + int length = ::GetWindowTextLength(reinterpret_cast(wd)); if(length > 0) { - native_string_type str; //One for NULL terminator which GetWindowText will write. str.resize(length+1); @@ -1122,9 +1156,11 @@ namespace nana{ //Remove the null terminator writtien by GetWindowText str.resize(length); - - return str; } + + if (!is_current_thread) + lock.forward(); + #elif defined(NANA_X11) nana::detail::platform_scope_guard psg; ::XTextProperty txtpro; @@ -1136,14 +1172,13 @@ namespace nana{ { if(size > 1) { - std::string text = *strlist; + str = *strlist; ::XFreeStringList(strlist); - return text; } } } #endif - return native_string_type(); + return str; } void native_interface::capture_window(native_window_type wd, bool cap) @@ -1305,7 +1340,10 @@ namespace nana{ if(::GetCurrentThreadId() != ::GetWindowThreadProcessId(reinterpret_cast(wd), nullptr)) ::PostMessage(reinterpret_cast(wd), nana::detail::messages::async_set_focus, 0, 0); else + { + internal_revert_guard revert; ::SetFocus(reinterpret_cast(wd)); + } } #elif defined(NANA_X11) nana::detail::platform_scope_guard lock; @@ -1414,6 +1452,11 @@ namespace nana{ if(static_cast(y) > sz.height + ext_height) sz.height = static_cast(y); } +#else + //eliminate unused parameter compiler warning. + static_cast(ext_width); + static_cast(ext_height); + static_cast(true_for_max); #endif return sz; } diff --git a/source/gui/detail/window_layout.cpp b/source/gui/detail/window_layout.cpp index f44be0a6..ea574baa 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-2015 Jinhao(cnjinhao@hotmail.com) +* Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -24,7 +24,7 @@ namespace nana //class window_layout void window_layout::paint(core_window_t* wd, bool is_redraw, bool is_child_refreshed) { - if (wd->flags.refreshing) + if (is_redraw && wd->flags.refreshing) return; if (nullptr == wd->effect.bground) @@ -53,7 +53,7 @@ namespace nana //get the root graphics auto& graph = *(wd->root_graph); - if (wd->other.category != category::lite_widget_tag::value) + if (category::flags::lite_widget != wd->other.category) graph.bitblt(vr, wd->drawer.graphics, nana::point(vr.x - wd->pos_root.x, vr.y - wd->pos_root.y)); _m_paste_children(wd, is_child_refreshed, have_refreshed, vr, graph, nana::point()); @@ -66,13 +66,15 @@ namespace nana nana::point p_src; for (auto & el : blocks) { - if (el.window->other.category == category::frame_tag::value) +#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; @@ -95,9 +97,9 @@ namespace nana } //read_visual_rectangle - //@brief: Reads the visual rectangle of a window, the visual rectangle's reference frame is to root widget, - // the visual rectangle is a rectangular block that a window should be displayed on screen. - // The result is a rectangle that is a visible area for its ancesters. + ///@brief Reads the visual rectangle of a window, the visual rectangle's reference frame is to root widget, + /// the visual rectangle is a rectangular block that a window should be displayed on screen. + /// The result is a rectangle that is a visible area for its ancesters. bool window_layout::read_visual_rectangle(core_window_t* wd, nana::rectangle& visual) { if (! wd->displayed()) return false; @@ -108,7 +110,7 @@ namespace nana { //Test if the root widget is overlapped the specified widget //the pos of root widget is (0, 0) - if (overlap(visual, rectangle{ wd->root_widget->pos_owner, wd->root_widget->dimension }) == false) + if (overlapped(visual, rectangle{ wd->root_widget->pos_owner, wd->root_widget->dimension }) == false) return false; } @@ -158,7 +160,7 @@ namespace nana bool window_layout::enable_effects_bground(core_window_t * wd, bool enabled) { - if (wd->other.category != category::widget_tag::value) + if (category::flags::widget != wd->other.category) return false; if (false == enabled) @@ -199,11 +201,11 @@ namespace nana nana::point rpos{ wd->pos_root }; auto & glass_buffer = wd->other.glass_buffer; - if (wd->parent->other.category == category::lite_widget_tag::value) + if (category::flags::lite_widget == wd->parent->other.category) { std::vector layers; core_window_t * beg = wd->parent; - while (beg && (beg->other.category == category::lite_widget_tag::value)) + while (beg && (category::flags::lite_widget == beg->other.category)) { layers.push_back(beg); beg = beg->parent; @@ -229,7 +231,7 @@ namespace nana nana::rectangle ovlp; if (child->visible && overlap(r, rectangle(child->pos_owner, child->dimension), ovlp)) { - if (child->other.category != category::lite_widget_tag::value) + if (category::flags::lite_widget != child->other.category) glass_buffer.bitblt(nana::rectangle(ovlp.x - pre->pos_owner.x, ovlp.y - pre->pos_owner.y, ovlp.width, ovlp.height), child->drawer.graphics, nana::point(ovlp.x - child->pos_owner.x, ovlp.y - child->pos_owner.y)); ovlp.x += pre->pos_root.x; ovlp.y += pre->pos_root.y; @@ -250,7 +252,7 @@ namespace nana nana::rectangle ovlp; if (child->visible && overlap(r_of_wd, rectangle{ child->pos_owner, child->dimension }, ovlp)) { - if (child->other.category != category::lite_widget_tag::value) + if (category::flags::lite_widget != child->other.category) glass_buffer.bitblt(nana::rectangle{ ovlp.x - wd->pos_owner.x, ovlp.y - wd->pos_owner.y, ovlp.width, ovlp.height }, child->drawer.graphics, nana::point(ovlp.x - child->pos_owner.x, ovlp.y - child->pos_owner.y)); ovlp.x += wd->pos_root.x; @@ -285,7 +287,7 @@ namespace nana if (overlap(nana::rectangle{ child->pos_root, child->dimension }, parent_rect, rect)) { bool have_child_refreshed = false; - if (child->other.category != category::lite_widget_tag::value) + if (category::flags::lite_widget != child->other.category) { if (is_child_refreshed && (false == child->flags.refreshing)) { @@ -357,7 +359,7 @@ namespace nana //_m_notify_glasses //@brief: Notify the glass windows that are overlapped with the specified vis_rect - void window_layout::_m_notify_glasses(core_window_t* const sigwd, const nana::rectangle& r_visual) + void window_layout::_m_notify_glasses(core_window_t* const sigwd, const nana::rectangle& /*r_visual*/) { typedef category::flags cat_flags; @@ -365,7 +367,7 @@ namespace nana for (auto wd : data_sect.effects_bground_windows) { if (wd == sigwd || !wd->displayed() || - (false == overlap(nana::rectangle{ wd->pos_root, wd->dimension }, r_of_sigwd))) + (false == overlapped(nana::rectangle{ wd->pos_root, wd->dimension }, r_of_sigwd))) continue; if (sigwd->parent == wd->parent) diff --git a/source/gui/detail/window_manager.cpp b/source/gui/detail/window_manager.cpp index 4aa19757..2fedf0d7 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-2015 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -86,13 +86,12 @@ namespace detail private: std::vector table_; }; - //class window_manager + //class window_manager struct window_handle_deleter { void operator()(basic_window* wd) const { - bedrock::instance().evt_operation().umake(reinterpret_cast(wd)); delete wd; } }; @@ -238,7 +237,7 @@ namespace detail switch(evtid) { case event_code::mouse_drop: - wd->flags.dropable = (is_make || (0 != wd->together.events_ptr->mouse_dropfiles.length())); + wd->flags.dropable = (is_make || (0 != wd->annex.events_ptr->mouse_dropfiles.length())); break; default: break; @@ -276,10 +275,14 @@ namespace detail if (impl_->wd_register.available(owner)) { if (owner->flags.destroying) - throw std::logic_error("the specified owner is destory"); + throw std::runtime_error("the specified owner is destory"); +#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; } @@ -290,7 +293,7 @@ namespace detail auto result = native_interface::create_window(native, nested, r, app); if (result.native_handle) { - core_window_t* wd = new core_window_t(owner, widget_notifier_interface::get_notifier(wdg), (category::root_tag**)nullptr); + auto wd = new core_window_t(owner, widget_notifier_interface::get_notifier(wdg), (category::root_tag**)nullptr); if (nested) { wd->owner = nullptr; @@ -312,8 +315,10 @@ namespace detail wd->bind_native_window(result.native_handle, result.width, result.height, result.extra_width, result.extra_height, value->root_graph); impl_->wd_register.insert(wd, wd->thread_id); - if (owner && owner->other.category == category::frame_tag::value) +#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); @@ -322,6 +327,7 @@ 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! @@ -338,13 +344,14 @@ namespace detail 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(frame->other.category == category::frame_tag::value) + if(category::flags::frame == frame->other.category) frame->other.attribute.frame->attach.push_back(wd); return true; } @@ -357,9 +364,9 @@ namespace detail { //Thread-Safe Required! std::lock_guard lock(mutex_); - if(frame->other.category == category::frame_tag::value) + if(category::flags::frame == frame->other.category) { - if (impl_->wd_register.available(wd) && wd->other.category == category::root_tag::value && wd->root != frame->root) + 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; @@ -368,6 +375,7 @@ namespace detail } return false; } +#endif window_manager::core_window_t* window_manager::create_widget(core_window_t* parent, const rectangle& r, bool is_lite, widget* wdg) { @@ -399,7 +407,7 @@ namespace detail if (wd->flags.destroying) return; - if(wd->other.category == category::root_tag::value) + if(category::flags::root == wd->other.category) { auto &brock = bedrock::instance(); arg_unload arg; @@ -456,15 +464,17 @@ namespace detail update(parent, false, false, &update_area); } - //destroy_handle - //@brief: Delete window handle, the handle type must be a root and a frame. void window_manager::destroy_handle(core_window_t* wd) { //Thread-Safe Required! std::lock_guard lock(mutex_); if (impl_->wd_register.available(wd) == false) return; - if((wd->other.category == category::root_tag::value) || (wd->other.category != category::frame_tag::value)) +#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); @@ -484,7 +494,7 @@ namespace detail std::lock_guard lock(mutex_); if (impl_->wd_register.available(wd)) { - if(wd->other.category == category::root_tag::value) + if(category::flags::root == wd->other.category) native_interface::window_icon(wd->root, small_icon, big_icon); } } @@ -501,16 +511,20 @@ namespace detail if(visible != wd->visible) { +#ifndef WIDGET_FRAME_DEPRECATED native_window_type nv = nullptr; switch(wd->other.category) { - case category::root_tag::value: + case category::flags::root: nv = wd->root; break; - case category::frame_tag::value: + 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); @@ -703,7 +717,7 @@ namespace detail wd->dimension = sz; - if(category::lite_widget_tag::value != wd->other.category) + if(category::flags::lite_widget != wd->other.category) { bool graph_state = wd->drawer.graphics.empty(); wd->drawer.graphics.make(sz); @@ -714,18 +728,20 @@ namespace detail if(graph_state != wd->drawer.graphics.empty()) wd->drawer.typeface_changed(); - if(category::root_tag::value == wd->other.category) + if(category::flags::root == wd->other.category) { wd->root_graph->make(sz); if(false == passive) native_interface::window_size(wd->root, sz + nana::size(wd->extra_width, wd->extra_height)); } - else if(category::frame_tag::value == wd->other.category) +#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 { //update the bground buffer of glass window. @@ -778,15 +794,7 @@ namespace detail parent = parent->parent; } - //Copy the root buffer that wd specified into DeviceContext -#if defined(NANA_LINUX) || defined(NANA_MACOS) - wd->drawer.map(reinterpret_cast(wd), forced, update_area); -#elif defined(NANA_WINDOWS) - if(nana::system::this_thread_id() == wd->thread_id) - wd->drawer.map(reinterpret_cast(wd), forced, update_area); - else - bedrock::instance().map_thread_root_buffer(wd, forced, update_area); -#endif + bedrock::instance().flush_surface(wd, forced, update_area); } } @@ -810,6 +818,12 @@ namespace detail this->map(wd, forced, update_area); return true; } + else if (forced) + { + window_layer::paint(wd, false, false); + this->map(wd, true, update_area); + return true; + } } else if (redraw) window_layer::paint(wd, true, false); @@ -833,31 +847,34 @@ namespace detail //do_lazy_refresh //@brief: defined a behavior of flush the screen //@return: it returns true if the wnd is available - bool window_manager::do_lazy_refresh(core_window_t* wd, bool force_copy_to_screen) + bool window_manager::do_lazy_refresh(core_window_t* wd, bool force_copy_to_screen, bool refresh_tree) { //Thread-Safe Required! std::lock_guard lock(mutex_); - //It's not worthy to redraw if visible is false if (false == impl_->wd_register.available(wd)) return false; + //It's not worthy to redraw if visible is false if(wd->visible && (!wd->is_draw_through())) { if (wd->visible_parents()) { if ((wd->other.upd_state == core_window_t::update_state::refresh) || force_copy_to_screen) { - window_layer::paint(wd, false, false); + window_layer::paint(wd, false, refresh_tree); this->map(wd, force_copy_to_screen); } else if (effects::edge_nimbus::none != wd->effect.edge_nimbus) { - this->map(wd, true); + //The window is still mapped because of edge nimbus effect. + //Avoid duplicate copy if action state is not changed and the window is not focused. + if ((wd->flags.action != wd->flags.action_before) || (bedrock::instance().focus() == wd)) + this->map(wd, true); } } else - window_layer::paint(wd, true, false); //only refreshing if it has an invisible parent + window_layer::paint(wd, true, refresh_tree); //only refreshing if it has an invisible parent } wd->other.upd_state = core_window_t::update_state::none; return true; @@ -925,7 +942,7 @@ namespace detail //set_focus //@brief: set a keyboard focus to a window. this may fire a focus event. - window_manager::core_window_t* window_manager::set_focus(core_window_t* wd, bool root_has_been_focused) + window_manager::core_window_t* window_manager::set_focus(core_window_t* wd, bool root_has_been_focused, arg_focus::reason reason) { //Thread-Safe Required! std::lock_guard lock(mutex_); @@ -945,12 +962,13 @@ namespace detail if (impl_->wd_register.available(prev_focus)) { - if(prev_focus->together.caret) - prev_focus->together.caret->set_active(false); + if(prev_focus->annex.caret_ptr) + prev_focus->annex.caret_ptr->activate(false); arg.getting = false; arg.window_handle = reinterpret_cast(prev_focus); arg.receiver = wd->root; + arg.focus_reason = arg_focus::reason::general; brock.emit(event_code::focus, prev_focus, arg, true, brock.get_thread_context()); } @@ -962,12 +980,13 @@ namespace detail return prev_focus; //no new focus_window - if(wd->together.caret) - wd->together.caret->set_active(true); + if(wd->annex.caret_ptr) + wd->annex.caret_ptr->activate(true); arg.window_handle = reinterpret_cast(wd); arg.getting = true; arg.receiver = wd->root; + arg.focus_reason = reason; brock.emit(event_code::focus, wd, arg, true, brock.get_thread_context()); if (!root_has_been_focused) @@ -976,7 +995,7 @@ namespace detail //A fix by Katsuhisa Yuasa //The menubar token window will be redirected to the prev focus window when the new //focus window is a menubar. - //The focus window will be restore to the prev focus which losts the focus becuase of + //The focus window will be restored to the prev focus which losts the focus becuase of //memberbar. if (prev_focus && (wd == wd->root_widget->other.attribute.root->menubar)) wd = prev_focus; @@ -999,11 +1018,6 @@ namespace detail return attr_.capture.window; } - void window_manager::capture_ignore_children(bool ignore) - { - attr_.capture.ignore_children = ignore; - } - bool window_manager::capture_window_entered(int root_x, int root_y, bool& prev) { if(attr_.capture.window) @@ -1024,19 +1038,15 @@ namespace detail return attr_.capture.window; } - //capture_window - //@brief: set a window that always captures the mouse event if it is not in the range of window - //@return: this function dose return the previous captured window. If the wnd set captured twice, - // the return value is NULL - window_manager::core_window_t* window_manager::capture_window(core_window_t* wd, bool value) + void window_manager::capture_window(core_window_t* wd, bool captured, bool ignore_children) { if (!this->available(wd)) - return nullptr; + return; nana::point pos = native_interface::cursor_position(); auto & attr_cap = attr_.capture.history; - if(value) + if (captured) { if(wd != attr_.capture.window) { @@ -1046,19 +1056,17 @@ namespace detail if (impl_->wd_register.available(wd)) { wd->flags.captured = true; - native_interface::capture_window(wd->root, value); - auto prev = attr_.capture.window; - if(prev && (prev != wd)) - attr_cap.emplace_back(prev, attr_.capture.ignore_children); + native_interface::capture_window(wd->root, captured); + + if (attr_.capture.window) + attr_cap.emplace_back(attr_.capture.window, attr_.capture.ignore_children); attr_.capture.window = wd; - attr_.capture.ignore_children = true; + attr_.capture.ignore_children = ignore_children; native_interface::calc_window_point(wd->root, pos); attr_.capture.inside = _m_effective(wd, pos); - return prev; } } - return attr_.capture.window; } else if(wd == attr_.capture.window) { @@ -1093,10 +1101,7 @@ namespace detail break; } } - - return attr_.capture.window; } - return wd; } //enable_tabstop @@ -1357,7 +1362,7 @@ namespace detail if (established) { if (check_tree(wd, attr_.capture.window)) - capture_window(attr_.capture.window, false); + capture_window(attr_.capture.window, false, false); //The 3rd parameter is ignored if (root_attr->focus && check_tree(wd, root_attr->focus)) root_attr->focus = nullptr; @@ -1370,7 +1375,7 @@ namespace detail else { if (wd == attr_.capture.window) - capture_window(attr_.capture.window, false); + capture_window(attr_.capture.window, false, false); //The 3rd parameter is ignored. if (root_attr->focus == wd) root_attr->focus = nullptr; @@ -1379,7 +1384,7 @@ namespace detail root_attr->menubar = nullptr; } - if (wd->other.category == category::root_tag::value) + if (wd->other.category == category::flags::root) { root_runtime(wd->root)->shortkeys.clear(); wd->other.attribute.root->focus = nullptr; @@ -1412,9 +1417,10 @@ namespace detail if (!established) { + using effect_renderer = detail::edge_nimbus_renderer; + //remove the window from edge nimbus effect when it is destroying - using edge_nimbus = detail::edge_nimbus_renderer; - edge_nimbus::instance().erase(wd); + effect_renderer::instance().erase(wd); } else if (pa_root_attr != root_attr) { @@ -1459,7 +1465,8 @@ namespace detail } } - if (wd->other.category == category::frame_tag::value) +#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); @@ -1467,6 +1474,7 @@ namespace detail if (established) pa_root_attr->frames.push_back(wd); } +#endif if (established) { @@ -1520,11 +1528,11 @@ namespace detail wd->flags.destroying = true; - if(wd->together.caret) + if(wd->annex.caret_ptr) { //The deletion of caret wants to know whether the window is destroyed under SOME platform. Such as X11 - delete wd->together.caret; - wd->together.caret = nullptr; + delete wd->annex.caret_ptr; + wd->annex.caret_ptr = nullptr; } arg_destroy arg; @@ -1565,7 +1573,8 @@ namespace detail wd->drawer.detached(); wd->widget_notifier->destroy(); - if(wd->other.category == category::frame_tag::value) +#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. @@ -1574,6 +1583,7 @@ namespace detail 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); @@ -1584,13 +1594,18 @@ 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->together.caret && wd->together.caret->visible()) - wd->together.caret->update(); + 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); @@ -1616,7 +1631,7 @@ namespace detail for(auto i = wd->children.rbegin(); i != wd->children.rend(); ++i) { core_window_t* child = *i; - if((child->other.category != category::root_tag::value) && _m_effective(child, pos)) + if((child->other.category != category::flags::root) && _m_effective(child, pos)) { child = _m_find(child, pos); if(child) diff --git a/source/gui/dragger.cpp b/source/gui/dragger.cpp index 43a56ba2..459d9833 100644 --- a/source/gui/dragger.cpp +++ b/source/gui/dragger.cpp @@ -47,7 +47,7 @@ namespace nana API::umake_event(t.over); API::umake_event(t.release); API::umake_event(t.destroy); - API::capture_window(t.wd, false); + API::release_capture(t.wd); } } @@ -87,7 +87,8 @@ namespace nana { case event_code::mouse_down: dragging_ = true; - API::capture_window(arg.window_handle, true); + API::set_capture(arg.window_handle, true); + origin_ = API::cursor_position(); for (auto & t : targets_) { @@ -133,7 +134,8 @@ namespace nana } break; case event_code::mouse_up: - API::capture_window(arg.window_handle, false); + API::release_capture(arg.window_handle); + dragging_ = false; break; default: @@ -151,7 +153,7 @@ namespace nana if (i->wd == arg.window_handle) { triggers_.erase(i); - API::capture_window(arg.window_handle, false); + API::release_capture(arg.window_handle); return; } } diff --git a/source/gui/element.cpp b/source/gui/element.cpp index b2fdbb7f..48adbbfc 100644 --- a/source/gui/element.cpp +++ b/source/gui/element.cpp @@ -212,7 +212,7 @@ namespace nana class menu_crook : public crook_interface { - bool draw(graph_reference graph, const ::nana::color&, const ::nana::color& fgcolor, const nana::rectangle& r, element_state es, const data& crook_data) override + bool draw(graph_reference graph, const ::nana::color&, const ::nana::color& fgcolor, const nana::rectangle& r, element_state, const data& crook_data) override { if(crook_data.check_state == state::unchecked) return true; @@ -266,7 +266,7 @@ namespace nana class border_depressed : public border_interface { - bool draw(graph_reference graph, const ::nana::color& bgcolor, const ::nana::color& fgcolor, const ::nana::rectangle& r, element_state estate, unsigned weight) + bool draw(graph_reference graph, const ::nana::color& bgcolor, const ::nana::color&, const ::nana::rectangle& r, element_state estate, unsigned) { graph.rectangle(r, false, static_cast((element_state::focus_hovered == estate || element_state::focus_normal == estate) ? 0x0595E2 : 0x999A9E)); graph.rectangle(::nana::rectangle(r).pare_off(1), false, bgcolor); @@ -277,7 +277,7 @@ namespace nana class arrow_solid_triangle : public arrow_interface { - bool draw(graph_reference graph, const ::nana::color& bgcolor, const ::nana::color& fgcolor, const ::nana::rectangle& r, element_state estate, direction dir) override + bool draw(graph_reference graph, const ::nana::color&, const ::nana::color&, const ::nana::rectangle& r, element_state, direction dir) override { ::nana::point pos{ r.x + 3, r.y + 3 }; switch (dir) @@ -318,7 +318,7 @@ namespace nana class arrow_hollow_triangle : public arrow_interface { - bool draw(graph_reference graph, const ::nana::color& bgcolor, const ::nana::color& fgcolor, const ::nana::rectangle& r, element_state estate, ::nana::direction dir) override + bool draw(graph_reference graph, const ::nana::color&, const ::nana::color&, const ::nana::rectangle& r, element_state, ::nana::direction dir) override { int x = r.x + 3; int y = r.y + 3; @@ -364,7 +364,7 @@ namespace nana class arrowhead : public arrow_interface { - bool draw(graph_reference graph, const ::nana::color& bgcolor, const ::nana::color& fgcolor, const ::nana::rectangle& r, element_state estate, ::nana::direction dir) override + bool draw(graph_reference graph, const ::nana::color&, const ::nana::color&, const ::nana::rectangle& r, element_state, ::nana::direction dir) override { int x = r.x; int y = r.y + 5; @@ -425,7 +425,7 @@ namespace nana class arrow_double : public arrow_interface { - bool draw(graph_reference graph, const ::nana::color& bgcolor, const ::nana::color& fgcolor, const ::nana::rectangle& r, element_state estate, ::nana::direction dir) override + bool draw(graph_reference graph, const ::nana::color&, const ::nana::color&, const ::nana::rectangle& r, element_state, ::nana::direction dir) override { int x = r.x; int y = r.y; @@ -486,7 +486,7 @@ namespace nana class annex_button : public element_interface { - bool draw(graph_reference graph, const ::nana::color& arg_bgcolor, const ::nana::color& fgcolor, const rectangle& r, element_state estate) override + bool draw(graph_reference graph, const ::nana::color& arg_bgcolor, const ::nana::color&, const rectangle& r, element_state estate) override { auto bgcolor = arg_bgcolor; @@ -1300,7 +1300,7 @@ namespace nana if (stretch_all_) { if (from_r.width == to_r.width && from_r.height == to_r.height) - method_->paste(from_r, dst, to_r); + method_->paste(from_r, dst, to_r.position()); else method_->stretch(from_r, dst, to_r); @@ -1392,7 +1392,7 @@ namespace nana if (top_) { src_r.height = top_; - method_->paste(src_r, dst, to_r); + method_->paste(src_r, dst, to_r.position()); } if (bottom_) { diff --git a/source/gui/filebox.cpp b/source/gui/filebox.cpp index 5f8f1311..c18c7a83 100644 --- a/source/gui/filebox.cpp +++ b/source/gui/filebox.cpp @@ -12,7 +12,7 @@ #include #include -#include +#include #if defined(NANA_WINDOWS) #include @@ -24,12 +24,15 @@ #include #include #include - #include #include #include #include #endif +namespace fs = std::experimental::filesystem; +namespace fs_ext = nana::filesystem_ext; + + namespace nana { #if defined(NANA_POSIX) @@ -54,7 +57,7 @@ namespace nana friend listbox::oresolver& operator<<(listbox::oresolver& ores, const item_fs& item) { std::wstringstream tm; - tm<<(item.modified_time.tm_year + 1900)<<'-'; + tm<<(item.modified_time.tm_year + 1900)<<'-'; /// \todo : use nana::filesystem_ext:: pretty_file_date _m_add(tm, item.modified_time.tm_mon + 1)<<'-'; _m_add(tm, item.modified_time.tm_mday)<<' '; @@ -87,7 +90,7 @@ namespace nana return ss; } - static std::string _m_trans(std::size_t bytes) + static std::string _m_trans(std::size_t bytes) /// \todo : use nana::filesystem_ext::pretty_file_size { const char * ustr[] = {" KB", " MB", " GB", " TB"}; std::stringstream ss; @@ -142,13 +145,13 @@ namespace nana auto path = path_.caption(); auto root = path.substr(0, path.find('/')); if(root == "HOME") - path.replace(0, 4, nana::experimental::filesystem::path_user().native()); + path.replace(0, 4, fs_ext::path_user().native()); else if(root == "FILESYSTEM") path.erase(0, 10); else throw std::runtime_error("Nana.GUI.Filebox: Wrong categorize path"); - if(path.size() == 0) path = "/"; + if(path.size() == 0) path = "/"; /// \todo : use nana::filesystem_ext::def_rootstr? _m_load_cat_path(path); }); @@ -344,7 +347,7 @@ namespace nana else dir = saved_selected_path; - _m_load_cat_path(dir.size() ? dir : nana::experimental::filesystem::path_user().native()); + _m_load_cat_path(dir.size() ? dir : fs_ext::path_user().native()); tb_file_.caption(file_with_path_removed); } @@ -427,10 +430,8 @@ namespace nana nodes_.filesystem = tree_.insert("FS.ROOT", "Filesystem"); nodes_.filesystem.value(kind::filesystem); - namespace fs = ::nana::experimental::filesystem; - std::vector paths; - paths.emplace_back(fs::path_user().native()); + paths.emplace_back(fs_ext::path_user().native()); paths.emplace_back("/"); fs::directory_iterator end; @@ -474,7 +475,7 @@ namespace nana { auto begstr = path.substr(0, pos); if(begstr == "FS.HOME") - path.replace(0, 7, nana::experimental::filesystem::path_user().native()); + path.replace(0, 7, fs_ext::path_user().native()); else path.erase(0, pos); return begstr; @@ -490,8 +491,6 @@ namespace nana file_container_.clear(); - namespace fs = ::nana::experimental::filesystem; - fs::directory_iterator end; for(fs::directory_iterator i(path); i != end; ++i) { @@ -508,13 +507,13 @@ namespace nana { m.bytes = fs::file_size(path + m.name); m.directory = fs::is_directory(fattr); - ::nana::experimental::filesystem::modified_file_time(path + m.name, m.modified_time); + fs_ext::modified_file_time(path + m.name, m.modified_time); } else { m.bytes = 0; m.directory = fs::is_directory(*i); - ::nana::experimental::filesystem::modified_file_time(path + i->path().filename().native(), m.modified_time); + fs_ext::modified_file_time(path + i->path().filename().native(), m.modified_time); } file_container_.push_back(m); @@ -534,7 +533,7 @@ namespace nana while(!beg_node.empty() && (beg_node != nodes_.home) && (beg_node != nodes_.filesystem)) beg_node = beg_node.owner(); - auto head = nana::experimental::filesystem::path_user().native(); + auto head = fs_ext::path_user().native(); if(path.size() >= head.size() && (path.substr(0, head.size()) == head)) {//This is HOME path_.caption("HOME"); @@ -552,7 +551,6 @@ namespace nana if(head.size() == 0 || head[head.size() - 1] != '/') head += '/'; - namespace fs = ::nana::experimental::filesystem; fs::directory_iterator end; for(fs::directory_iterator i(head); i != end; ++i) @@ -649,20 +647,20 @@ namespace nana return; } - using file_type = nana::experimental::filesystem::file_type; + using file_type = fs::file_type; - experimental::filesystem::path fspath(fb_.addr_.filesystem + path); + fs::path fspath(fb_.addr_.filesystem + path); - auto fs = experimental::filesystem::status(fspath); + auto fst = fs::status(fspath); - if(fs.type() != file_type::not_found && fs.type() != file_type::none) + if(fst.type() != file_type::not_found && fst.type() != file_type::none) { mb<path = ipstr; } diff --git a/source/gui/layout_utility.cpp b/source/gui/layout_utility.cpp index c81273b3..4daade4a 100644 --- a/source/gui/layout_utility.cpp +++ b/source/gui/layout_utility.cpp @@ -16,7 +16,7 @@ namespace nana { //overlap test if overlaped between r1 and r2 - bool overlap(const rectangle& r1, const rectangle& r2) + bool overlapped(const rectangle& r1, const rectangle& r2) { if (r1.y + (long long)(r1.height) <= r2.y) return false; if(r1.y >= r2.y + (long long)(r2.height)) return false; @@ -30,7 +30,7 @@ namespace nana //overlap, compute the overlap area between r1 and r2. the rect is for root bool overlap(const rectangle& r1, const rectangle& r2, rectangle& r) { - if(overlap(r1, r2)) + if(overlapped(r1, r2)) { auto l1 = static_cast(r1.x) + r1.width; auto l2 = static_cast(r2.x) + r2.width; @@ -56,7 +56,7 @@ namespace nana if (overlap(ir, valid_r, op_ir) == false) return false; - valid_r = valid_dst_area; + valid_r.dimension(valid_dst_area); rectangle good_dr; if (overlap(dr, valid_r, good_dr) == false) return false; diff --git a/source/gui/msgbox.cpp b/source/gui/msgbox.cpp index dd20cf6e..6faefc4c 100644 --- a/source/gui/msgbox.cpp +++ b/source/gui/msgbox.cpp @@ -355,13 +355,15 @@ namespace nana msgbox::msgbox(const std::string& title) : wd_(nullptr), title_(title), button_(ok), icon_(icon_none) { - throw_not_utf8(title_); + // throw_not_utf8(title_); + review_utf8(title_); } msgbox::msgbox(window wd, const std::string& title, button_t b) : wd_(wd), title_(title), button_(b), icon_(icon_none) { - throw_not_utf8(title_); + // throw_not_utf8(title_); + review_utf8(title_); } msgbox& msgbox::icon(icon_t ic) @@ -702,13 +704,13 @@ namespace nana impl->spinbox.value(std::to_string(impl->value)); - impl->dock.events().resized.connect_unignorable([impl, label_px, value_px](const ::nana::arg_resized& arg) + impl->dock.events().resized.connect_unignorable([impl, label_px, value_px](const ::nana::arg_resized&) { impl->label.size({ label_px, 24 }); impl->spinbox.size({ value_px, 24 }); }); - impl->spinbox.events().destroy.connect_unignorable([impl] + impl->spinbox.events().destroy.connect_unignorable([impl](const arg_destroy&) { impl->value = impl->spinbox.to_int(); }); @@ -780,13 +782,13 @@ namespace nana impl->spinbox.value(std::to_string(impl->value)); - impl->dock.events().resized.connect_unignorable([impl, label_px, value_px](const ::nana::arg_resized& arg) + impl->dock.events().resized.connect_unignorable([impl, label_px, value_px](const ::nana::arg_resized&) { impl->label.size(::nana::size{ label_px, 24 }); impl->spinbox.size(::nana::size{ value_px, 24 }); }); - impl->spinbox.events().destroy.connect_unignorable([impl] + impl->spinbox.events().destroy.connect_unignorable([impl](const arg_destroy&) { impl->value = impl->spinbox.to_double(); }); @@ -913,7 +915,7 @@ namespace nana }); auto & wdg = (value_px ? static_cast(impl->combox) : static_cast(impl->textbox)); - wdg.events().destroy.connect_unignorable([&wdg, impl] + wdg.events().destroy.connect_unignorable([&wdg, impl](const arg_destroy&) { impl->value = wdg.caption(); }); @@ -1028,16 +1030,19 @@ namespace nana impl->wdg_year.size(sz); }); - impl->wdg_day.events().destroy.connect_unignorable([impl] + auto destroy_fn = [impl](const arg_destroy& arg) { - impl->day = impl->wdg_day.to_int(); - impl->month = static_cast(impl->wdg_month.option()) + 1; - }); + if (arg.window_handle == impl->wdg_day.handle()) + { + impl->day = impl->wdg_day.to_int(); + impl->month = static_cast(impl->wdg_month.option()) + 1; + } + else if(arg.window_handle == impl->wdg_year.handle()) + impl->year = impl->wdg_year.to_int(); + }; - impl->wdg_year.events().destroy.connect_unignorable([impl] - { - impl->year = impl->wdg_year.to_int(); - }); + impl->wdg_day.events().destroy.connect_unignorable(destroy_fn); + impl->wdg_year.events().destroy.connect_unignorable(destroy_fn); auto make_days = [impl] { @@ -1123,7 +1128,7 @@ namespace nana impl->browse.create(impl->dock); impl->browse.i18n(i18n_eval("Browse")); - impl->browse.events().click([wd, impl] + impl->browse.events().click.connect_unignorable([wd, impl](const arg_click&) { impl->fbox.owner(wd); if (impl->fbox.show()) @@ -1140,7 +1145,7 @@ namespace nana impl->browse.move({static_cast(arg.width - 60), 0, 60, arg.height}); }); - impl->path_edit.events().destroy.connect_unignorable([impl] + impl->path_edit.events().destroy.connect_unignorable([impl](const arg_destroy&) { impl->value = impl->path_edit.caption(); }); diff --git a/source/gui/notifier.cpp b/source/gui/notifier.cpp index 4e98e037..e46541c9 100644 --- a/source/gui/notifier.cpp +++ b/source/gui/notifier.cpp @@ -1,7 +1,7 @@ /* * Implementation of Notifier * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -33,9 +33,11 @@ #include #endif +#include "../paint/image_accessor.hpp" + namespace nana { - typedef std::lock_guard lock_guard; + using lock_guard = std::lock_guard; struct notifier::implement { @@ -47,13 +49,15 @@ namespace nana detail::notifier_events events; bool icon_added = false; std::size_t play_index; -#if defined(NANA_WINDOWS) - HICON icon_handle = nullptr; - std::vector icons; - void set_icon(HICON icon) + paint::image icon; + ::std::vector icons; + + void set_icon(const paint::image& ico) { - if (icon_handle) +#if defined(NANA_WINDOWS) + auto ico_handle = paint::image_accessor::icon(ico); + if (ico_handle) { NOTIFYICONDATA icon_data; memset(&icon_data, 0, sizeof icon_data); @@ -62,13 +66,15 @@ namespace nana icon_data.uID = id; icon_data.uFlags = NIF_MESSAGE | NIF_ICON; icon_data.uCallbackMessage = nana::detail::messages::tray; - icon_data.hIcon = icon; + icon_data.hIcon = ico_handle; ::Shell_NotifyIcon(icon_added ? NIM_MODIFY : NIM_ADD, &icon_data); icon_added = true; } - } +#else + static_cast(ico); #endif + } }; arg_notifier::operator nana::arg_mouse() const @@ -169,19 +175,19 @@ namespace nana switch (arg.evt_code) { case event_code::mouse_down: - evt_ptr->mouse_down.emit(arg); + evt_ptr->mouse_down.emit(arg, nullptr); break; case event_code::mouse_up: - evt_ptr->mouse_up.emit(arg); + evt_ptr->mouse_up.emit(arg, nullptr); break; case event_code::mouse_leave: - evt_ptr->mouse_leave.emit(arg); + evt_ptr->mouse_leave.emit(arg, nullptr); break; case event_code::mouse_move: - evt_ptr->mouse_move.emit(arg); + evt_ptr->mouse_move.emit(arg, nullptr); break; case event_code::dbl_click: - evt_ptr->dbl_click.emit(arg); + evt_ptr->dbl_click.emit(arg, nullptr); break; default: break; @@ -262,7 +268,7 @@ namespace nana #endif }); - impl_->evt_destroy = API::events(wd).destroy([this] + impl_->evt_destroy = API::events(wd).destroy.connect([this](const arg_destroy&) { close(); }); @@ -287,12 +293,6 @@ namespace nana icon_data.hWnd = reinterpret_cast(impl_->native_handle); icon_data.uID = impl_->id; ::Shell_NotifyIcon(NIM_DELETE, &icon_data); - - if (impl_->icon_handle) - ::DestroyIcon(impl_->icon_handle); - - for (auto handle : impl_->icons) - ::DestroyIcon(handle); #endif API::umake_event(impl_->evt_destroy); notifications::instance().cancel(impl_->native_handle, impl_->id); @@ -314,31 +314,30 @@ namespace nana ::Shell_NotifyIcon(impl_->icon_added ? NIM_MODIFY : NIM_ADD, &icon_data); impl_->icon_added = true; +#else + static_cast(str); //to eliminate unused parameter compiler warning. #endif } void notifier::icon(const std::string& icon_file) { -#if defined(NANA_WINDOWS) - auto pre_icon = impl_->icon_handle; - auto ico = (HICON)::LoadImageW(0, to_wstring(icon_file).data(), IMAGE_ICON, 0, 0, LR_LOADFROMFILE); - if (ico) + paint::image image_ico{ icon_file }; + auto icon_handle = paint::image_accessor::icon(image_ico); + if (icon_handle) { - impl_->icon_handle = ico; impl_->ani_timer.stop(); impl_->play_index = 0; - impl_->set_icon(impl_->icon_handle); - ::DestroyIcon(pre_icon); + impl_->set_icon(image_ico); + impl_->icon = image_ico; } -#endif } void notifier::insert_icon(const std::string& icon_file) { -#if defined(NANA_WINDOWS) - auto icon = (HICON)::LoadImage(0, to_wstring(icon_file).data(), IMAGE_ICON, 0, 0, LR_LOADFROMFILE); - impl_->icons.push_back(icon); -#endif + paint::image image_ico{ icon_file }; + auto icon_handle = paint::image_accessor::icon(image_ico); + if (icon_handle) + impl_->icons.emplace_back(static_cast(image_ico)); } void notifier::period(unsigned ms) @@ -352,6 +351,8 @@ namespace nana } else impl_->ani_timer.stop(); +#else + static_cast(ms); //to eliminate unused parameter compiler warning. #endif } diff --git a/source/gui/place.cpp b/source/gui/place.cpp index d6adb5d0..a10246f9 100644 --- a/source/gui/place.cpp +++ b/source/gui/place.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -304,9 +305,7 @@ namespace nana void _m_throw_error(const std::string& err) { - std::stringstream ss; - ss << "place: " << err << " at " << static_cast(sp_ - divstr_); - throw std::runtime_error(ss.str()); + throw std::runtime_error("nana::place: " + err + " at " + std::to_string(static_cast(sp_ - divstr_))); } void _m_attr_number_value() @@ -504,7 +503,7 @@ namespace nana static event_handle erase_element(std::vector& elements, window handle) { - for (auto i = elements.begin(), end = elements.end(); i != end; ++i) + for (auto i = elements.begin(); i != elements.end(); ++i) { if (i->handle == handle) { @@ -516,17 +515,13 @@ namespace nana return nullptr; } private: - //The defintion is moved after the definition of class division - template - void _m_for_each(division*, Function); - //Listen to destroy of a window //It will delete the element and recollocate when the window destroyed. event_handle _m_make_destroy(window wd) { - return API::events(wd).destroy.connect([this, wd](const arg_destroy& arg) + return API::events(wd).destroy.connect([this, wd](const arg_destroy&) { - for (auto i = elements.begin(), end = elements.end(); i != end; ++i) + if (erase_element(elements, wd)) { if (!API::is_destroying(API::get_parent_window(wd))) place_ptr_->collocate(); @@ -567,7 +562,7 @@ namespace nana //Listen to destroy of a window. The deleting a fastened window //does not change the layout. - auto evt = API::events(wd).destroy([this](const arg_destroy& arg) + auto evt = API::events(wd).destroy.connect([this](const arg_destroy& arg) { erase_element(fastened, arg.window_handle); }); @@ -608,10 +603,7 @@ namespace nana division(kind k, std::string&& n) : kind_of_division(k), - name(std::move(n)), - field(nullptr), - div_next(nullptr), - div_owner(nullptr) + name(std::move(n)) {} virtual ~division() @@ -749,7 +741,6 @@ namespace nana //Collocate the division and its children divisions, //The window parameter is specified for the window which the place object binded. virtual void collocate(window) = 0; - public: kind kind_of_division; bool display{ true }; @@ -764,18 +755,11 @@ namespace nana place_parts::margin margin; place_parts::repeated_array gap; - field_gather * field; - division * div_next, *div_owner; + field_gather * field{ nullptr }; + division * div_next{ nullptr }; + division * div_owner{ nullptr }; };//end class division - template - void place::implement::field_gather::_m_for_each(division * div, Function fn) - { - for (auto & up : div->children) //The element of children is unique_ptr - fn(up.get()); - } - - class place::implement::div_arrange : public division { @@ -1010,12 +994,14 @@ namespace nana auto find_lowest = [&revises](double level_px) { double v = (std::numeric_limits::max)(); + for (auto i = revises.begin(); i != revises.end(); ++i) { - if (i->min_px >= 0 && i->min_px < v && i->min_px > level_px) - v = i->min_px; - else if (i->max_px >= 0 && i->max_px < v) - v = i->max_px; + auto & rev = *i; + if (rev.min_px >= 0 && rev.min_px < v && rev.min_px > level_px) + v = rev.min_px; + else if (rev.max_px >= 0 && rev.max_px < v) + v = rev.max_px; } return v; }; @@ -1127,7 +1113,7 @@ namespace nana } } - void collocate(window wd) override + void collocate(window) override { if (!field || !(visible && display)) return; @@ -1323,8 +1309,6 @@ namespace nana public: div_splitter(place_parts::number_t init_weight) : division(kind::splitter, std::string()), - splitter_cursor_(cursor::arrow), - pause_move_collocate_(false), init_weight_(init_weight) { this->weight.assign(splitter_px); @@ -1346,74 +1330,88 @@ namespace nana splitter_.cursor(splitter_cursor_); dragger_.trigger(splitter_); - splitter_.events().mouse_down.connect_unignorable([this](const arg_mouse& arg) + + auto grab_fn = [this](const arg_mouse& arg) { if (false == arg.left_button) return; - begin_point_ = splitter_.pos(); - - auto px_ptr = &nana::rectangle::width; - - //Use field_area of leaf, not margin_area. Otherwise splitter would be at wrong position - auto area_left = _m_leaf_left()->field_area; - auto area_right = _m_leaf_right()->field_area; - - if (nana::cursor::size_we != splitter_cursor_) + if (event_code::mouse_down == arg.evt_code) { - left_pos_ = area_left.y; - right_pos_ = area_right.bottom(); - px_ptr = &nana::rectangle::height; + begin_point_ = splitter_.pos(); + + auto px_ptr = &nana::rectangle::width; + + //Use field_area of leaf, not margin_area. Otherwise splitter would be at wrong position + auto area_left = _m_leaf_left()->field_area; + auto area_right = _m_leaf_right()->field_area; + + if (nana::cursor::size_we != splitter_cursor_) + { + left_pos_ = area_left.y; + right_pos_ = area_right.bottom(); + px_ptr = &nana::rectangle::height; + } + else + { + left_pos_ = area_left.x; + right_pos_ = area_right.right(); + } + + left_pixels_ = area_left.*px_ptr; + right_pixels_ = area_right.*px_ptr; + + grabbed_ = true; } - else + else if(event_code::mouse_up == arg.evt_code) { - left_pos_ = area_left.x; - right_pos_ = area_right.right(); + grabbed_ = false; } + else if (event_code::mouse_move == arg.evt_code) + { + if(!grabbed_) + return; + + const bool vert = (::nana::cursor::size_we != splitter_cursor_); + auto area_px = rectangle_rotator(vert, div_owner->margin_area()).w(); + int delta = (vert ? splitter_.pos().y - begin_point_.y : splitter_.pos().x - begin_point_.x); - left_pixels_ = area_left.*px_ptr; - right_pixels_ = area_right.*px_ptr; - }); + int total_pixels = static_cast(left_pixels_ + right_pixels_); - splitter_.events().mouse_move.connect_unignorable([this](const arg_mouse& arg) - { - if (false == arg.left_button) - return; + auto left_px = static_cast(left_pixels_) + delta; + if (left_px > total_pixels) + left_px = total_pixels; + else if (left_px < 0) + left_px = 0; - const bool vert = (::nana::cursor::size_we != splitter_cursor_); - auto area_px = rectangle_rotator(vert, div_owner->margin_area()).w(); - int delta = (vert ? splitter_.pos().y - begin_point_.y : splitter_.pos().x - begin_point_.x); + double imd_rate = 100.0 / area_px; + left_px = static_cast(limit_px(_m_leaf_left(), left_px, area_px)); + _m_leaf_left()->weight.assign_percent(imd_rate * left_px); - int total_pixels = static_cast(left_pixels_ + right_pixels_); + auto right_px = static_cast(right_pixels_) - delta; + if (right_px > total_pixels) + right_px = total_pixels; + else if (right_px < 0) + right_px = 0; - auto left_px = static_cast(left_pixels_)+delta; - if (left_px > total_pixels) - left_px = total_pixels; - else if (left_px < 0) - left_px = 0; + right_px = static_cast(limit_px(_m_leaf_right(), right_px, area_px)); + _m_leaf_right()->weight.assign_percent(imd_rate * right_px); - double imd_rate = 100.0 / area_px; - left_px = static_cast(limit_px(_m_leaf_left(), left_px, area_px)); - _m_leaf_left()->weight.assign_percent(imd_rate * left_px); + pause_move_collocate_ = true; + div_owner->collocate(splitter_.parent()); - auto right_px = static_cast(right_pixels_)-delta; - if (right_px > total_pixels) - right_px = total_pixels; - else if (right_px < 0) - right_px = 0; + //After the collocating, the splitter keeps the calculated weight of left division, + //and clear the weight of right division. + _m_leaf_right()->weight.reset(); - right_px = static_cast(limit_px(_m_leaf_right(), right_px, area_px)); - _m_leaf_right()->weight.assign_percent(imd_rate * right_px); + pause_move_collocate_ = false; + } + }; - pause_move_collocate_ = true; - div_owner->collocate(splitter_.parent()); - - //After the collocating, the splitter keeps the calculated weight of left division, - //and clear the weight of right division. - _m_leaf_right()->weight.reset(); - - pause_move_collocate_ = false; - }); + auto & events = splitter_.events(); + events.mouse_down.connect_unignorable(grab_fn); + events.mouse_up.connect_unignorable(grab_fn); + events.mouse_move.connect_unignorable(grab_fn); } auto limited_range = _m_update_splitter_range(); @@ -1526,13 +1524,14 @@ namespace nana return area; } private: - nana::cursor splitter_cursor_; + nana::cursor splitter_cursor_{nana::cursor::arrow}; place_parts::splitter splitter_; nana::point begin_point_; int left_pos_, right_pos_; unsigned left_pixels_, right_pixels_; dragger dragger_; - bool pause_move_collocate_; //A flag represents whether do move when collocating. + bool grabbed_{ false }; + bool pause_move_collocate_{ false }; //A flag represents whether do move when collocating. place_parts::number_t init_weight_; }; @@ -1557,7 +1556,7 @@ namespace nana } } - void collocate(window wd) override + void collocate(window) override { if (!dockable_field) { @@ -1621,7 +1620,7 @@ namespace nana indicator_.docker->z_order(nullptr, ::nana::z_order_action::topmost); indicator_.docker->show(); - indicator_.docker->events().destroy([this] + indicator_.docker->events().destroy.connect([this](const arg_destroy&) { if (indicator_.dock_area) { @@ -1664,7 +1663,7 @@ namespace nana graph.blend(r, clr, 0.5); r.x = r.y = 0; - r = graph.size(); + r.dimension(graph.size()); r.width = border_px; graph.blend(r, clr, 0.5); r.x = right - border_px; @@ -1764,81 +1763,87 @@ namespace nana this->bgcolor(colors::alice_blue); this->cursor(_m_is_vert(dir_) ? ::nana::cursor::size_ns : ::nana::cursor::size_we); - this->events().mouse_down([this](const arg_mouse& arg) + + auto grab_fn = [this, wd](const arg_mouse& arg) { - if (arg.button != ::nana::mouse::left_button) - return; - - bool is_vert = _m_is_vert(dir_); - - API::capture_window(this->handle(), true); - auto basepos = API::cursor_position(); - base_pos_.x = (is_vert ? basepos.y : basepos.x); - - basepos = this->pos(); - base_pos_.y = (is_vert ? basepos.y : basepos.x); - - base_px_ = (is_vert ? pane_dv_->field_area.height : pane_dv_->field_area.width); - }); - - this->events().mouse_up([this] - { - API::capture_window(this->handle(), false); - }); - - this->events().mouse_move([this, wd](const arg_mouse& arg) - { - if (!arg.is_left_button()) - return; - - auto now_pos = API::cursor_position(); - int delta = (_m_is_vert(dir_) ? now_pos.y : now_pos.x) - base_pos_.x; - int new_pos = base_pos_.y + delta; - if (new_pos < range_.x) + if (event_code::mouse_down == arg.evt_code) //press mouse button { - new_pos = range_.x; - delta = new_pos - base_pos_.y; - } - else if (new_pos >= range_.y) - { - new_pos = range_.y - 1; - delta = new_pos - base_pos_.y; - } + if (arg.button != ::nana::mouse::left_button) + return; - now_pos = this->pos(); - if (_m_is_vert(dir_)) - now_pos.y = new_pos; + bool is_vert = _m_is_vert(dir_); + + this->set_capture(true); + + auto basepos = API::cursor_position(); + base_pos_.x = (is_vert ? basepos.y : basepos.x); + + basepos = this->pos(); + base_pos_.y = (is_vert ? basepos.y : basepos.x); + + base_px_ = (is_vert ? pane_dv_->field_area.height : pane_dv_->field_area.width); + } + else if (event_code::mouse_move == arg.evt_code) //hover + { + if (!arg.is_left_button()) + return; + + auto now_pos = API::cursor_position(); + int delta = (_m_is_vert(dir_) ? now_pos.y : now_pos.x) - base_pos_.x; + int new_pos = base_pos_.y + delta; + if (new_pos < range_.x) + { + new_pos = range_.x; + delta = new_pos - base_pos_.y; + } + else if (new_pos >= range_.y) + { + new_pos = range_.y - 1; + delta = new_pos - base_pos_.y; + } + + now_pos = this->pos(); + if (_m_is_vert(dir_)) + now_pos.y = new_pos; + else + now_pos.x = new_pos; + this->move(now_pos); + + auto px = base_px_; + switch (dir_) + { + case ::nana::direction::west: + case ::nana::direction::north: + if (delta < 0) + px -= static_cast(-delta); + else + px += static_cast(delta); + break; + case ::nana::direction::east: + case ::nana::direction::south: + if (delta < 0) + px += static_cast(-delta); + else + px -= static_cast(delta); + break; + default: + break; + } + + auto dock_px = (_m_is_vert(dir_) ? dock_dv_->field_area.height : dock_dv_->field_area.width); + + pane_dv_->weight.assign_percent(double(px) / double(dock_px) * 100); + + dock_dv_->collocate(wd); + } else - now_pos.x = new_pos; - this->move(now_pos); + this->release_capture(); + }; - auto px = base_px_; - switch (dir_) - { - case ::nana::direction::west: - case ::nana::direction::north: - if (delta < 0) - px -= static_cast(-delta); - else - px += static_cast(delta); - break; - case ::nana::direction::east: - case ::nana::direction::south: - if (delta < 0) - px += static_cast(-delta); - else - px -= static_cast(delta); - break; - default: - break; - } - - auto dock_px = (_m_is_vert(dir_) ? dock_dv_->field_area.height : dock_dv_->field_area.width); - - pane_dv_->weight.assign_percent(double(px) / double(dock_px) * 100); - - dock_dv_->collocate(wd); - }); + auto & evt = this->events(); + evt.mouse_down.connect(grab_fn); + evt.mouse_up.connect(grab_fn); + evt.mouse_move.connect(grab_fn); } void range(int begin, int end) @@ -1862,11 +1867,12 @@ namespace nana division* front() const { - for (auto i = children.cbegin(); i != children.cend(); ++i) + for (auto & child : children) { - if (i->get()->display) - return i->get(); + if (child->display) + return child.get(); } + return nullptr; } @@ -2072,7 +2078,7 @@ namespace nana { if (root_division && window_handle) { - root_division->field_area = API::window_size(window_handle); + root_division->field_area.dimension(API::window_size(window_handle)); if (root_division->field_area.empty()) return; @@ -2156,7 +2162,7 @@ namespace nana { auto splitter = new div_splitter(tknizer.number()); children.back()->div_next = splitter; - children.emplace_back(splitter); + children.emplace_back(std::unique_ptr{ splitter }); } break; case token::div_start: @@ -2165,7 +2171,7 @@ namespace nana if (!children.empty()) children.back()->div_next = div.get(); - children.emplace_back(div.release()); + children.emplace_back(std::move(div)); } break; case token::vert: @@ -2405,7 +2411,7 @@ namespace nana auto dockpn = new div_dockpane(std::move(child->name), this, child->dir); dockpn->div_owner = child->div_owner; dockpn->weight = child->weight; - adjusted_children.emplace_back(dockpn); + adjusted_children.emplace_back(std::unique_ptr{ dockpn }); } division * next = nullptr; @@ -2563,7 +2569,7 @@ namespace nana { if (impl_->root_division) { - impl_->root_division->field_area = ::nana::size(arg.width, arg.height); + impl_->root_division->field_area.dimension({ arg.width, arg.height }); impl_->root_division->collocate(arg.window_handle); } }); @@ -2621,11 +2627,11 @@ namespace nana implement::division * div_next = div_ptr->div_next; if (div_owner) { - for (auto i = div_owner->children.begin(); i != div_owner->children.end(); ++i) + for (auto& child: div_owner->children) { - if (i->get() == div_ptr) + if (child.get() == div_ptr) { - replaced = &(*i); + replaced = &child; break; } } @@ -2677,7 +2683,7 @@ namespace nana { //search the division with the specified name, //and attached the division to the field - implement::division * div = implement::search_div_name(impl_->root_division.get(), name); + auto div = implement::search_div_name(impl_->root_division.get(), name); if (div) { if (div->field && (div->field != p)) @@ -2778,12 +2784,11 @@ namespace nana //Register the factory if it has a name if (!factory_name.empty()) { - auto i = impl_->dock_factoris.find(factory_name); - if (i != impl_->dock_factoris.end()) + if (impl_->dock_factoris.find(factory_name) != impl_->dock_factoris.end()) throw std::invalid_argument("nana::place - the specified factory name(" + factory_name + ") already exists"); impl_->dock_factoris[factory_name] = dock_ptr; - dock_ptr->factories[factory_name].swap(factory); + dock_ptr->factories[factory_name] = std::move(factory); } auto div = dynamic_cast(impl_->search_div_name(impl_->root_division.get(), name)); @@ -2812,7 +2817,7 @@ namespace nana return *this; } - place& place::dock_create(const std::string& factory) + widget* place::dock_create(const std::string& factory) { auto i = impl_->dock_factoris.find(factory); if (i == impl_->dock_factoris.end()) @@ -2832,10 +2837,12 @@ namespace nana dock_ptr->dockarea->move(dock_ptr->attached->field_area); } - dock_ptr->dockarea->add_pane(i->second->factories[factory]); + return dock_ptr->dockarea->add_pane(i->second->factories[factory]); } - return *this; + return nullptr; } //end class place }//end namespace nana + +#include diff --git a/source/gui/place_parts.hpp b/source/gui/place_parts.hpp index 25708b93..2bd29c1d 100644 --- a/source/gui/place_parts.hpp +++ b/source/gui/place_parts.hpp @@ -1,7 +1,7 @@ /* * Parts of Class Place * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -97,7 +97,7 @@ namespace nana graph.rectangle(r, true, xclr); } - + r.x += (r.width - 16) / 2; r.y = (r.height - 16) / 2; @@ -109,14 +109,14 @@ namespace nana x_pointed_ = _m_button_area().is_hit(arg.pos); refresh(graph); - API::lazy_refresh(); + API::dev::lazy_refresh(); } void mouse_leave(graph_reference graph, const arg_mouse&) override { x_pointed_ = false; refresh(graph); - API::lazy_refresh(); + API::dev::lazy_refresh(); } void mouse_down(graph_reference graph, const arg_mouse&) override @@ -127,7 +127,7 @@ namespace nana x_state_ = ::nana::mouse_action::pressed; refresh(graph); - API::lazy_refresh(); + API::dev::lazy_refresh(); } void mouse_up(graph_reference graph, const arg_mouse&) override @@ -135,9 +135,9 @@ namespace nana if (!x_pointed_) return; - x_state_ = ::nana::mouse_action::over; + x_state_ = ::nana::mouse_action::hovered; refresh(graph); - API::lazy_refresh(); + API::dev::lazy_refresh(); close_fn_(); } @@ -164,10 +164,7 @@ namespace nana : public widget_object < category::widget_tag, dockcaption_dtrigger > { public: - void on_close(std::function fn) - { - get_drawer_trigger().on_close(std::move(fn)); - } + using widget_object::get_drawer_trigger; }; class dockarea @@ -192,7 +189,7 @@ namespace nana base_type::create(parent, true); this->caption("dockarea"); caption_.create(*this, true); - caption_.on_close([this] + caption_.get_drawer_trigger().on_close([this] { bool destroy_dockarea = true; @@ -203,12 +200,11 @@ namespace nana destroy_dockarea = (0 == tabbar_->length()); } - if (destroy_dockarea) notifier_->request_close(); }); - this->events().resized([this](const arg_resized& arg) + this->events().resized.connect([this](const arg_resized& arg) { rectangle r{ 0, 0, arg.width, 20 }; caption_.move(r); @@ -224,7 +220,6 @@ namespace nana else r.height = arg.height - 20; } - for (auto & pn : panels_) { @@ -233,51 +228,110 @@ namespace nana } }); - caption_.events().mouse_down([this](const arg_mouse& arg) + auto grab_fn = [this](const arg_mouse& arg) { - if (::nana::mouse::left_button == arg.button) + if (event_code::mouse_down == arg.evt_code) { - moves_.started = true; - moves_.start_pos = API::cursor_position(); - moves_.start_container_pos = (floating() ? container_->pos() : this->pos()); - API::capture_window(caption_, true); - } - }); - - caption_.events().mouse_move([this](const arg_mouse& arg) - { - if (arg.left_button && moves_.started) - { - auto move_pos = API::cursor_position() - moves_.start_pos; - if (!floating()) + if (::nana::mouse::left_button == arg.button) { - if (std::abs(move_pos.x) > 4 || std::abs(move_pos.y) > 4) - float_away(move_pos); - } - else - { - move_pos += moves_.start_container_pos; - API::move_window(container_->handle(), move_pos); - notifier_->notify_move(); + moves_.started = true; + moves_.start_pos = API::cursor_position(); + moves_.start_container_pos = (floating() ? container_->pos() : this->pos()); + caption_.set_capture(true); } } - }); - - caption_.events().mouse_up([this](const arg_mouse& arg) - { - if ((::nana::mouse::left_button == arg.button) && moves_.started) + else if (event_code::mouse_move == arg.evt_code) { - moves_.started = false; - API::capture_window(caption_, false); - notifier_->notify_move_stopped(); + if (arg.left_button && moves_.started) + { + auto move_pos = API::cursor_position() - moves_.start_pos; + if (!floating()) + { + if (std::abs(move_pos.x) > 4 || std::abs(move_pos.y) > 4) + float_away(move_pos); + } + else + { + move_pos += moves_.start_container_pos; + API::move_window(container_->handle(), move_pos); + notifier_->notify_move(); + } + } } - }); + else if (event_code::mouse_up == arg.evt_code) + { + if ((::nana::mouse::left_button == arg.button) && moves_.started) + { + moves_.started = false; + caption_.release_capture(); + notifier_->notify_move_stopped(); + } + } + }; + + caption_.events().mouse_down.connect(grab_fn); + caption_.events().mouse_move.connect(grab_fn); + caption_.events().mouse_up.connect(grab_fn); } - void add_pane(factory & fn) + widget* add_pane(factory & fn) { - rectangle r{ point(), this->size()}; + auto fn_ptr = &fn; + widget * w = nullptr; + API::dev::affinity_execute(*this, [this, fn_ptr, &w] + { + w=_m_add_pane(*fn_ptr); + }); + return w; + } + + void float_away(const ::nana::point& move_pos) + { + if (container_) + return; + + caption_.release_capture(); + + rectangle r{ pos() + move_pos, size() }; + container_.reset(new form(host_window_, r.pare_off(-1), form::appear::bald())); + drawing dw(container_->handle()); + dw.draw([](paint::graphics& graph) + { + graph.rectangle(false, colors::coral); + }); + + API::set_parent_window(handle(), container_->handle()); + this->move({ 1, 1 }); + + container_->events().resized.connect([this](const arg_resized& arg) + { + this->size({arg.width - 2, arg.height - 2}); + }); + + container_->show(); + caption_.set_capture(true); + + notifier_->notify_float(); + } + + void dock() + { + caption_.release_capture(); + + API::set_parent_window(handle(), host_window_); + container_.reset(); + notifier_->notify_dock(); + } + + bool floating() const + { + return (nullptr != container_); + } + private: + widget* _m_add_pane(factory & fn) + { + rectangle r{ point(), this->size() }; //get a rectangle excluding caption r.y = 20; @@ -293,22 +347,21 @@ namespace nana tabbar_.reset(new tabbar_lite(*this)); tabbar_->events().selected.clear(); - tabbar_->events().selected([this] + tabbar_->events().selected.connect([this](const event_arg&) { auto handle = tabbar_->attach(tabbar_->selected()); - if (handle) - caption_.caption(API::window_caption(handle)); - else - caption_.caption(::std::string()); + //Set caption through a caption of window specified by handle + //Empty if handle is null + caption_.caption(API::window_caption(handle)); }); - tabbar_->move({ 0, r.bottom() - 20, r.width, 20 }); r.height -= 20; + tabbar_->move({ 0, r.bottom(), r.width, 20 }); std::size_t pos = 0; for (auto & pn : panels_) { - tabbar_->push_back(::nana::charset(pn.widget_ptr->caption())); + tabbar_->push_back(pn.widget_ptr->caption()); tabbar_->attach(pos++, *pn.widget_ptr); } } @@ -317,70 +370,31 @@ namespace nana r.height -= 20; auto wdg = fn(*this); - - if (tabbar_) + if (wdg) { - tabbar_->push_back(::nana::charset(wdg->caption())); - tabbar_->attach(panels_.size(), wdg->handle()); + if (tabbar_) + { + tabbar_->push_back(::nana::charset(wdg->caption())); + tabbar_->attach(panels_.size(), wdg->handle()); + } + + if (panels_.empty()) + { + caption_.caption(wdg->caption()); + } + + panels_.emplace_back(); + auto wdg_ptr = wdg.get(); + panels_.back().widget_ptr.swap(wdg); + + for (auto & pn : panels_) + { + if (pn.widget_ptr) + pn.widget_ptr->move(r); + } + return wdg_ptr; } - - if (panels_.empty()) - { - caption_.caption(wdg->caption()); - } - - panels_.emplace_back(); - panels_.back().widget_ptr.swap(wdg); - - for (auto & pn : panels_) - { - if (pn.widget_ptr) - pn.widget_ptr->move(r); - } - } - - void float_away(const ::nana::point& move_pos) - { - if (container_) - return; - - API::capture_window(caption_, false); - - rectangle r{ pos() + move_pos, size() }; - container_.reset(new form(host_window_, r.pare_off(-1), form::appear::bald())); - drawing dw(container_->handle()); - dw.draw([](paint::graphics& graph) - { - graph.rectangle(false, colors::coral); - }); - - - - API::set_parent_window(handle(), container_->handle()); - this->move({ 1, 1 }); - - container_->events().resized([this](const arg_resized& arg) - { - this->size({arg.width - 2, arg.height - 2}); - }); - - container_->show(); - API::capture_window(caption_, true); - - notifier_->notify_float(); - } - - void dock() - { - API::capture_window(caption_, false); - API::set_parent_window(handle(), host_window_); - container_.reset(); - notifier_->notify_dock(); - } - - bool floating() const - { - return (nullptr != container_); + return nullptr; } private: window host_window_{nullptr}; diff --git a/source/gui/programming_interface.cpp b/source/gui/programming_interface.cpp index a5f96e98..787a2183 100644 --- a/source/gui/programming_interface.cpp +++ b/source/gui/programming_interface.cpp @@ -55,9 +55,9 @@ namespace API namespace detail { - ::nana::widget_colors* make_scheme(::nana::detail::scheme_factory_base&& factory) + ::nana::widget_geometrics* make_scheme(::nana::detail::scheme_factory_interface&& factory) { - return restrict::bedrock.make_scheme(static_cast<::nana::detail::scheme_factory_base&&>(factory)); + return restrict::bedrock.scheme().create(static_cast<::nana::detail::scheme_factory_interface&&>(factory)); } bool emit_event(event_code evt_code, window wd, const ::nana::event_arg& arg) @@ -89,7 +89,7 @@ namespace API if (!restrict::wd_manager().available(reinterpret_cast(wd))) return nullptr; - return reinterpret_cast(wd)->together.events_ptr.get(); + return reinterpret_cast(wd)->annex.events_ptr.get(); } }//end namespace detail @@ -104,8 +104,7 @@ namespace API { if (iwd->effect.edge_nimbus == effects::edge_nimbus::none) { - basic_window::edge_nimbus_action ena = { iwd }; - cont.push_back(ena); + cont.push_back(basic_window::edge_nimbus_action{ iwd, false}); } iwd->effect.edge_nimbus = static_cast(static_cast(en) | static_cast(iwd->effect.edge_nimbus)); } @@ -180,31 +179,35 @@ namespace API namespace dev { + void affinity_execute(window window_handle, const std::function& fn) + { + interface_type::affinity_execute(root(window_handle), fn); + } + bool set_events(window wd, const std::shared_ptr& gep) { auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - if (restrict::wd_manager().available(iwd) && iwd->set_events(gep)) - { - restrict::bedrock.evt_operation().make(wd, gep); - return true; - } + + if (restrict::wd_manager().available(iwd)) + iwd->set_events(gep); return false; + } - void set_scheme(window wd, widget_colors* wdg_colors) + void set_scheme(window wd, widget_geometrics* wdg_geom) { auto iwd = reinterpret_cast(wd); internal_scope_guard lock; if (restrict::wd_manager().available(iwd)) - iwd->scheme = wdg_colors; + iwd->annex.scheme = wdg_geom; } - widget_colors* get_scheme(window wd) + widget_geometrics* get_scheme(window wd) { auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - return (restrict::wd_manager().available(iwd) ? iwd->scheme : nullptr); + return (restrict::wd_manager().available(iwd) ? iwd->annex.scheme : nullptr); } void attach_drawer(widget& wd, drawer_trigger& dr) @@ -214,7 +217,7 @@ namespace API if(restrict::wd_manager().available(iwd)) { iwd->drawer.graphics.make(iwd->dimension); - iwd->drawer.graphics.rectangle(true, iwd->scheme->background.get_color()); + iwd->drawer.graphics.rectangle(true, iwd->annex.scheme->background.get_color()); iwd->drawer.attached(wd, dr); iwd->drawer.refresh(); //Always redrawe no matter it is visible or invisible. This can make the graphics data correctly. } @@ -263,10 +266,12 @@ 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) { @@ -315,6 +320,11 @@ namespace API if (restrict::wd_manager().available(iwd)) iwd->flags.space_click_enabled = enable; } + + void lazy_refresh() + { + restrict::bedrock.thread_context_lazy_refresh(); + } }//end namespace dev @@ -326,7 +336,7 @@ namespace API return nullptr; } - //exit + //close all windows in current thread void exit() { @@ -363,6 +373,42 @@ namespace API interface_type::close_window(i); } } + //close all windows + void exit_all() + { + std::vector v; + + internal_scope_guard lock; + restrict::wd_manager().all_handles(v); + if (v.size()) + { + std::vector roots; + native_window_type root = nullptr; + //unsigned tid = nana::system::this_thread_id(); + for (auto wd : v) + { + if (/*(wd->thread_id == tid) &&*/ (wd->root != root)) + { + root = wd->root; + bool exists = false; + for (auto i = roots.cbegin(); i != roots.cend(); ++i) + { + if (*i == root) + { + exists = true; + break; + } + } + + if (!exists) + roots.push_back(root); + } + } + + for (auto i : roots) + interface_type::close_window(i); + } + } //transform_shortkey_text //@brief: This function searchs whether the text contains a '&' and removes the character for transforming. @@ -428,7 +474,7 @@ namespace API nana::point pos{ r.x, r.y }; calc_window_point(wd, pos); - r = pos; + r.position(pos); return r; } @@ -502,6 +548,7 @@ 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); @@ -527,6 +574,7 @@ namespace API } return nullptr; } +#endif void close_window(window wd) { @@ -825,13 +873,6 @@ namespace API return (restrict::wd_manager().available(iwd) ? iwd->flags.enabled : false); } - //lazy_refresh: - //@brief: A widget drawer draws the widget surface in answering an event. This function will tell the drawer to copy the graphics into window after event answering. - void lazy_refresh() - { - restrict::bedrock.thread_context_lazy_refresh(); - } - //refresh_window //@brief: Refresh the window and display it immediately. void refresh_window(window wd) @@ -929,7 +970,7 @@ namespace API void focus_window(window wd) { - restrict::wd_manager().set_focus(reinterpret_cast(wd), false); + restrict::wd_manager().set_focus(reinterpret_cast(wd), false, arg_focus::reason::general); restrict::wd_manager().update(reinterpret_cast(wd), false, false); } @@ -938,16 +979,15 @@ namespace API return reinterpret_cast(restrict::wd_manager().capture_window()); } - window capture_window(window wd, bool value) + void set_capture(window wd, bool ignore_children) { - return reinterpret_cast( - restrict::wd_manager().capture_window(reinterpret_cast(wd), value) - ); + restrict::wd_manager().capture_window(reinterpret_cast(wd), true, ignore_children); } - void capture_ignore_children(bool ignore) + void release_capture(window wd) { - restrict::wd_manager().capture_ignore_children(ignore); + //The 3rd parameter is useless when the 2nd parameter is false. + restrict::wd_manager().capture_window(reinterpret_cast(wd), false, false); } void modal_window(window wd) @@ -986,7 +1026,7 @@ namespace API { internal_scope_guard lock; if (restrict::wd_manager().available(reinterpret_cast(wd))) - return reinterpret_cast(wd)->scheme->foreground.get_color(); + return reinterpret_cast(wd)->annex.scheme->foreground.get_color(); return{}; } @@ -996,10 +1036,10 @@ namespace API internal_scope_guard lock; if (restrict::wd_manager().available(iwd)) { - auto prev = iwd->scheme->foreground.get_color(); + auto prev = iwd->annex.scheme->foreground.get_color(); if (prev != clr) { - iwd->scheme->foreground = clr; + iwd->annex.scheme->foreground = clr; restrict::wd_manager().update(iwd, true, false); } return prev; @@ -1011,7 +1051,7 @@ namespace API { internal_scope_guard lock; if (restrict::wd_manager().available(reinterpret_cast(wd))) - return reinterpret_cast(wd)->scheme->background.get_color(); + return reinterpret_cast(wd)->annex.scheme->background.get_color(); return{}; } @@ -1021,10 +1061,10 @@ namespace API internal_scope_guard lock; if (restrict::wd_manager().available(iwd)) { - auto prev = iwd->scheme->background.get_color(); + auto prev = iwd->annex.scheme->background.get_color(); if (prev != clr) { - iwd->scheme->background = clr; + iwd->annex.scheme->background = clr; //If the bground mode of this window is basic, it should remake the background if (iwd->effect.bground && iwd->effect.bground_fade_rate < 0.01) // fade rate < 0.01 means it is basic mode @@ -1041,7 +1081,7 @@ namespace API { internal_scope_guard lock; if (restrict::wd_manager().available(reinterpret_cast(wd))) - return reinterpret_cast(wd)->scheme->activated.get_color(); + return reinterpret_cast(wd)->annex.scheme->activated.get_color(); return{}; } @@ -1051,10 +1091,10 @@ namespace API internal_scope_guard lock; if (restrict::wd_manager().available(iwd)) { - auto prev = iwd->scheme->activated.get_color(); + auto prev = iwd->annex.scheme->activated.get_color(); if (prev != clr) { - iwd->scheme->activated = clr; + iwd->annex.scheme->activated = clr; restrict::wd_manager().update(iwd, true, false); } return prev; @@ -1063,12 +1103,99 @@ namespace API return{}; } - void create_caret(window wd, unsigned width, unsigned height) + class caret_proxy + : public caret_interface + { + public: + caret_proxy(basic_window* wd) + : window_{ wd } + {} + + void disable_throw() noexcept override + { + throw_ = false; + } + + void effective_range(const rectangle& range) override + { + internal_scope_guard lock; + auto caret = _m_caret(); + if (caret) + caret->effective_range(range); + } + + void position(const point& pos) override + { + internal_scope_guard lock; + auto caret = _m_caret(); + if (caret) + caret->position(pos); + } + + point position() const override + { + internal_scope_guard lock; + auto caret = _m_caret(); + if (caret) + return caret->position(); + + return{}; + } + + void dimension(const size& size) override + { + internal_scope_guard lock; + auto caret = _m_caret(); + if (caret) + caret->dimension(size); + } + + size dimension() const override + { + internal_scope_guard lock; + auto caret = _m_caret(); + if (caret) + return caret->dimension(); + + return{}; + } + + void visible(bool visibility) override + { + internal_scope_guard lock; + auto caret = _m_caret(); + if (caret) + caret->visible(visibility); + } + + bool visible() const override + { + internal_scope_guard lock; + auto caret = _m_caret(); + return (caret && caret->visible()); + } + private: + caret_interface* _m_caret() const + { + if (restrict::wd_manager().available(window_) && window_->annex.caret_ptr) + return window_->annex.caret_ptr; + + if (throw_) + throw std::runtime_error("nana.api: access invalid caret"); + + return nullptr; + } + private: + basic_window* const window_; + bool throw_{ true }; + }; + + void create_caret(window wd, const size& caret_size) { auto iwd = reinterpret_cast(wd); internal_scope_guard lock; - if(restrict::wd_manager().available(iwd) && (nullptr == iwd->together.caret)) - iwd->together.caret = new ::nana::detail::caret_descriptor(iwd, width, height); + if (restrict::wd_manager().available(iwd) && !(iwd->annex.caret_ptr)) + iwd->annex.caret_ptr = new ::nana::detail::caret(iwd, caret_size); } void destroy_caret(window wd) @@ -1077,72 +1204,19 @@ namespace API internal_scope_guard lock; if(restrict::wd_manager().available(iwd)) { - auto p = iwd->together.caret; - iwd->together.caret = nullptr; + auto p = iwd->annex.caret_ptr; + iwd->annex.caret_ptr = nullptr; delete p; } } - void caret_pos(window wd, const point& pos) - { - auto iwd = reinterpret_cast(wd); - internal_scope_guard lock; - if(restrict::wd_manager().available(iwd) && iwd->together.caret) - iwd->together.caret->position(pos.x, pos.y); - } + std::unique_ptr open_caret(window window_handle, bool disable_throw) + { + auto p = new caret_proxy{ reinterpret_cast(window_handle) }; + if (disable_throw) + p->disable_throw(); - nana::point caret_pos(window wd) - { - auto iwd = reinterpret_cast(wd); - internal_scope_guard lock; - if(restrict::wd_manager().available(iwd) && iwd->together.caret) - return iwd->together.caret->position(); - - return{}; - } - - void caret_effective_range(window wd, const nana::rectangle& rect) - { - auto iwd = reinterpret_cast(wd); - internal_scope_guard lock; - if(restrict::wd_manager().available(iwd) && iwd->together.caret) - iwd->together.caret->effective_range(rect); - } - - void caret_size(window wd, const nana::size& sz) - { - auto iwd = reinterpret_cast(wd); - internal_scope_guard isg; - if(restrict::wd_manager().available(iwd) && iwd->together.caret) - iwd->together.caret->size(sz); - } - - nana::size caret_size(window wd) - { - auto iwd = reinterpret_cast(wd); - internal_scope_guard lock; - if(restrict::wd_manager().available(iwd) && iwd->together.caret) - return iwd->together.caret->size(); - - return{}; - } - - void caret_visible(window wd, bool is_show) - { - auto iwd = reinterpret_cast(wd); - internal_scope_guard lock; - if(restrict::wd_manager().available(iwd) && iwd->together.caret) - iwd->together.caret->visible(is_show); - } - - bool caret_visible(window wd) - { - auto iwd = reinterpret_cast(wd); - internal_scope_guard lock; - if(restrict::wd_manager().available(iwd) && iwd->together.caret) - return iwd->together.caret->visible(); - - return false; + return std::unique_ptr{ p }; } void tabstop(window wd) @@ -1171,7 +1245,7 @@ namespace API window move_tabstop(window wd, bool next) { basic_window* ts_wd = restrict::wd_manager().tabstop(reinterpret_cast(wd), next); - restrict::wd_manager().set_focus(ts_wd, false); + restrict::wd_manager().set_focus(ts_wd, false, arg_focus::reason::general); restrict::wd_manager().update(ts_wd, false, false); return reinterpret_cast(ts_wd); } @@ -1322,7 +1396,7 @@ namespace API { case nana::mouse_action::normal: return (is_focused ? nana::element_state::focus_normal : nana::element_state::normal); - case nana::mouse_action::over: + case nana::mouse_action::hovered: return (is_focused ? nana::element_state::focus_hovered : nana::element_state::hovered); case nana::mouse_action::pressed: return nana::element_state::pressed; diff --git a/source/gui/screen.cpp b/source/gui/screen.cpp index 1a0be98b..da390bda 100644 --- a/source/gui/screen.cpp +++ b/source/gui/screen.cpp @@ -167,6 +167,8 @@ namespace nana } } } +#else + static_cast(pos); //to eliminate unused parameter compiler warning. #endif return get_primary(); } diff --git a/source/gui/timer.cpp b/source/gui/timer.cpp index 58a6700e..589d0107 100644 --- a/source/gui/timer.cpp +++ b/source/gui/timer.cpp @@ -135,7 +135,7 @@ namespace nana void emit(const arg_elapse& arg) { - evt_elapse_.emit(arg); + evt_elapse_.emit(arg, nullptr); } private: const timer_identifier timer_; diff --git a/source/gui/tooltip.cpp b/source/gui/tooltip.cpp index ac76b7d6..e4a03634 100644 --- a/source/gui/tooltip.cpp +++ b/source/gui/tooltip.cpp @@ -227,7 +227,7 @@ namespace nana window_->tooltip_move(API::cursor_position(), true); } - void show_duration(window wd, point pos, const std::string& text, std::size_t duration) + void show_duration(window /*wd*/, point pos, const std::string& text, std::size_t duration) { if (nullptr == window_ || window_->tooltip_empty()) { @@ -282,17 +282,22 @@ namespace nana } auto & events = API::events(wd); - events.mouse_enter.connect([this](const arg_mouse& arg){ - auto & pr = _m_get(arg.window_handle); - if (pr.second.size()) - this->show(pr.second); - }); - auto leave_fn = [this]{ - this->close(); + auto mouse_fn = [this](const arg_mouse& arg) + { + if (event_code::mouse_enter == arg.evt_code) + { + auto & pr = _m_get(arg.window_handle); + if (pr.second.size()) + this->show(pr.second); + } + else + this->close(); }; - events.mouse_leave.connect(leave_fn); - events.mouse_down.connect(leave_fn); + + events.mouse_enter.connect(mouse_fn); + events.mouse_leave.connect(mouse_fn); + events.mouse_down.connect(mouse_fn); events.destroy.connect([this](const arg_destroy& arg){ _m_untip(arg.window_handle); diff --git a/source/gui/widgets/button.cpp b/source/gui/widgets/button.cpp index 301c07c7..f4a1ab3f 100644 --- a/source/gui/widgets/button.cpp +++ b/source/gui/widgets/button.cpp @@ -134,7 +134,7 @@ namespace nana{ namespace drawerbase { attr_.e_state = (attr_.pushed || attr_.keep_pressed ? element_state::pressed : element_state::hovered); refresh(graph); - API::lazy_refresh(); + API::dev::lazy_refresh(); } void trigger::mouse_leave(graph_reference graph, const arg_mouse&) @@ -144,7 +144,7 @@ namespace nana{ namespace drawerbase attr_.e_state = element_state::normal; refresh(graph); - API::lazy_refresh(); + API::dev::lazy_refresh(); } void trigger::mouse_down(graph_reference graph, const arg_mouse& arg) @@ -163,7 +163,7 @@ namespace nana{ namespace drawerbase _m_press(graph, false); } - void trigger::key_press(graph_reference graph, const arg_keyboard& arg) + void trigger::key_press(graph_reference, const arg_keyboard& arg) { bool ch_tabstop_next; switch(arg.key) @@ -185,7 +185,7 @@ namespace nana{ namespace drawerbase { attr_.focused = arg.getting; refresh(graph); - API::lazy_refresh(); + API::dev::lazy_refresh(); } void trigger::_m_draw_title(graph_reference graph, bool enabled) @@ -321,11 +321,12 @@ namespace nana{ namespace drawerbase attr_.e_state = element_state::pressed; attr_.keep_pressed = true; - API::capture_window(*wdg_, true); + wdg_->set_capture(true); } else { - API::capture_window(*wdg_, false); + wdg_->release_capture(); + attr_.keep_pressed = false; if (attr_.enable_pushed && (false == attr_.pushed)) { @@ -342,7 +343,7 @@ namespace nana{ namespace drawerbase } refresh(graph); - API::lazy_refresh(); + API::dev::lazy_refresh(); } void trigger::emit_click() @@ -479,7 +480,7 @@ namespace nana{ namespace drawerbase void button::_m_complete_creation() { - events().shortkey.connect_unignorable([this] + events().shortkey.connect_unignorable([this](const arg_keyboard&) { get_drawer_trigger().emit_click(); }); diff --git a/source/gui/widgets/categorize.cpp b/source/gui/widgets/categorize.cpp index 3c10b41f..cf29e267 100644 --- a/source/gui/widgets/categorize.cpp +++ b/source/gui/widgets/categorize.cpp @@ -89,7 +89,7 @@ namespace nana if(ui_el_.what == ui_el_.item_root) { - _m_item_bground(graph, r.x + 1, r.y, r.width - 2, r.height, (state == mouse_action::pressed ? mouse_action::pressed : mouse_action::over)); + _m_item_bground(graph, r.x + 1, r.y, r.width - 2, r.height, (state == mouse_action::pressed ? mouse_action::pressed : mouse_action::hovered)); graph.rectangle(r, false, static_cast(0x3C7FB1)); if(state == mouse_action::pressed) { @@ -114,8 +114,8 @@ namespace nana mouse_action state_arrow, state_name; if(mouse_action::pressed != state) { - state_arrow = (ui_el_.what == ui_el_.item_arrow ? mouse_action::over : mouse_action::normal); - state_name = (ui_el_.what == ui_el_.item_name ? mouse_action::over : mouse_action::normal); + state_arrow = (ui_el_.what == ui_el_.item_arrow ? mouse_action::hovered : mouse_action::normal); + state_name = (ui_el_.what == ui_el_.item_name ? mouse_action::hovered : mouse_action::normal); } else { @@ -170,7 +170,7 @@ namespace nana nana::color clr_top(static_cast(0xEAEAEA)), clr_bottom(static_cast(0xDCDCDC)); switch(state) { - case mouse_action::over: + case mouse_action::hovered: clr_top.from_rgb(0xdf, 0xf2, 0xfc); clr_bottom.from_rgb(0xa9, 0xda, 0xf5); break; @@ -571,7 +571,7 @@ namespace nana style_.listbox = &(form_loader()(window_, r, true)); style_.listbox->set_module(style_.module, 16); - style_.listbox->events().destroy.connect_unignorable([this] + style_.listbox->events().destroy.connect_unignorable([this](const arg_destroy&) { //Close list when listbox is destoryed style_.mode = mode::normal; @@ -881,7 +881,7 @@ namespace nana { scheme_->mouse_pressed(); scheme_->draw(graph); - API::lazy_refresh(); + API::dev::lazy_refresh(); } } } @@ -894,7 +894,7 @@ namespace nana { scheme_->mouse_release(); scheme_->draw(graph); - API::lazy_refresh(); + API::dev::lazy_refresh(); } } } @@ -904,7 +904,7 @@ namespace nana if(scheme_->locate(arg.pos.x, arg.pos.y) && API::window_enabled(scheme_->window_handle())) { scheme_->draw(graph); - API::lazy_refresh(); + API::dev::lazy_refresh(); } } @@ -913,7 +913,7 @@ namespace nana if(API::window_enabled(scheme_->window_handle()) && (scheme_->is_list_shown() == false) && scheme_->erase_locate()) { scheme_->draw(graph); - API::lazy_refresh(); + API::dev::lazy_refresh(); } } //end class trigger diff --git a/source/gui/widgets/checkbox.cpp b/source/gui/widgets/checkbox.cpp index 4a14a7fc..f5980da9 100644 --- a/source/gui/widgets/checkbox.cpp +++ b/source/gui/widgets/checkbox.cpp @@ -85,7 +85,7 @@ namespace nana{ namespace drawerbase void drawer::mouse_down(graph_reference graph, const arg_mouse&) { refresh(graph); - API::lazy_refresh(); + API::dev::lazy_refresh(); } void drawer::mouse_up(graph_reference graph, const arg_mouse&) @@ -94,22 +94,22 @@ namespace nana{ namespace drawerbase { impl_->crook.reverse(); arg_checkbox arg{ static_cast(impl_->widget_ptr) }; - API::events(impl_->widget_ptr->handle()).checked.emit(arg); + API::events(impl_->widget_ptr->handle()).checked.emit(arg, impl_->widget_ptr->handle()); } refresh(graph); - API::lazy_refresh(); + API::dev::lazy_refresh(); } void drawer::mouse_enter(graph_reference graph, const arg_mouse&) { refresh(graph); - API::lazy_refresh(); + API::dev::lazy_refresh(); } void drawer::mouse_leave(graph_reference graph, const arg_mouse&) { refresh(graph); - API::lazy_refresh(); + API::dev::lazy_refresh(); } drawer::implement * drawer::impl() const @@ -172,7 +172,7 @@ namespace nana{ namespace drawerbase API::refresh_window(handle()); arg_checkbox arg(this); - this->events().checked.emit(arg); + this->events().checked.emit(arg, this->handle()); } } @@ -216,7 +216,7 @@ namespace nana{ namespace drawerbase uiobj.check(false); uiobj.react(false); - element_tag el = {}; + element_tag el; el.uiobj = &uiobj; diff --git a/source/gui/widgets/combox.cpp b/source/gui/widgets/combox.cpp index c5df35f8..3b494683 100644 --- a/source/gui/widgets/combox.cpp +++ b/source/gui/widgets/combox.cpp @@ -1,7 +1,7 @@ /* * A Combox Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -41,7 +41,7 @@ namespace nana void text_changed() override { - widget_.events().text_changed.emit(::nana::arg_combox{ widget_ }); + widget_.events().text_changed.emit(::nana::arg_combox{ widget_ }, widget_); } private: ::nana::combox & widget_; @@ -171,7 +171,7 @@ namespace nana if(editor_) { editor_->editable(enb); - + editor_->show_caret(enb); if (!enb) { editor_->ext_renderer().background = [this](graph_reference graph, const ::nana::rectangle&, const ::nana::color&) @@ -245,7 +245,7 @@ namespace nana void open_lister_if_push_button_positioned() { - if((nullptr == state_.lister) && !items_.empty() && (parts::push_button == state_.pointer_where)) + if((nullptr == state_.lister) && !items_.empty() && (!editor_->attr().editable || (parts::push_button == state_.pointer_where))) { module_.items.clear(); std::copy(items_.cbegin(), items_.cend(), std::back_inserter(module_.items)); @@ -253,12 +253,25 @@ namespace nana state_.lister->renderer(item_renderer_); state_.lister->set_module(module_, image_pixels_); state_.item_index_before_selection = module_.index; + //The lister window closes by itself. I just take care about the destroy event. //The event should be destroy rather than unload. Because the unload event is invoked while //the lister is not closed, if popuping a message box, the lister will cover the message box. - state_.lister->events().destroy.connect_unignorable([this] + state_.lister->events().destroy.connect_unignorable([this](const arg_destroy&) { - _m_lister_close_sig(); + state_.lister = nullptr; //The lister closes by itself. + if ((module_.index != nana::npos) && (module_.index != state_.item_index_before_selection)) + { + option(module_.index, true); + API::update_window(*widget_); + } + else + { + //Redraw the widget even though the index has not been changed, + //because the push button should be updated due to the state + //changed from pressed to normal/hovered. + API::refresh_window(*widget_); + } }); } } @@ -338,11 +351,11 @@ namespace nana if (calc_where(*graph_, pos.x, pos.y)) state_.button_state = element_state::normal; - editor_->text(::nana::charset(items_[index]->item_text, ::nana::unicode::utf8)); + editor_->text(::nana::charset(items_[index]->item_text, ::nana::unicode::utf8), false); _m_draw_push_button(widget_->enabled()); _m_draw_image(); - widget_->events().selected.emit(::nana::arg_combox(*widget_)); + widget_->events().selected.emit(::nana::arg_combox(*widget_), widget_->handle()); } } @@ -351,7 +364,7 @@ namespace nana std::size_t pos = 0; for (auto & m : items_) { - if (m->key && detail::pred_equal_by_less(m->key.get(), p.get())) + if (m->key && detail::pred_equal(m->key.get(), p.get())) return pos; ++pos; } @@ -371,7 +384,7 @@ namespace nana std::size_t pos = 0; for (auto & m : items_) { - if (m->key && detail::pred_equal_by_less(m->key.get(), kv)) + if (m->key && detail::pred_equal(m->key.get(), kv)) { erase(pos); return; @@ -432,23 +445,6 @@ namespace nana return true; } private: - void _m_lister_close_sig() - { - state_.lister = nullptr; //The lister closes by itself. - if ((module_.index != nana::npos) && (module_.index != state_.item_index_before_selection)) - { - option(module_.index, true); - API::update_window(*widget_); - } - else - { - //Redraw the widget even though the index has not been changed, - //because the push button should be updated due to the state - //changed from pressed to normal/hovered. - API::refresh_window(*widget_); - } - } - void _m_draw_push_button(bool enabled) { ::nana::rectangle r{graph_->size()}; @@ -590,7 +586,7 @@ namespace nana { drawer_->draw(); drawer_->editor()->reset_caret(); - API::lazy_refresh(); + API::dev::lazy_refresh(); } } @@ -600,7 +596,7 @@ namespace nana if(drawer_->widget_ptr()->enabled()) { drawer_->draw(); - API::lazy_refresh(); + API::dev::lazy_refresh(); } } @@ -611,36 +607,36 @@ namespace nana if(drawer_->widget_ptr()->enabled()) { drawer_->draw(); - API::lazy_refresh(); + API::dev::lazy_refresh(); } } - void trigger::mouse_down(graph_reference graph, const arg_mouse& arg) + void trigger::mouse_down(graph_reference, const arg_mouse& arg) { //drawer_->set_mouse_press(true); drawer_->set_button_state(element_state::pressed, false); if(drawer_->widget_ptr()->enabled()) { auto * editor = drawer_->editor(); - if (!editor->mouse_pressed(arg)) - drawer_->open_lister_if_push_button_positioned(); + editor->mouse_pressed(arg); + drawer_->open_lister_if_push_button_positioned(); drawer_->draw(); if(editor->attr().editable) editor->reset_caret(); - API::lazy_refresh(); + API::dev::lazy_refresh(); } } - void trigger::mouse_up(graph_reference graph, const arg_mouse& arg) + void trigger::mouse_up(graph_reference, const arg_mouse& arg) { if (drawer_->widget_ptr()->enabled() && !drawer_->has_lister()) { drawer_->editor()->mouse_pressed(arg); drawer_->set_button_state(element_state::hovered, false); drawer_->draw(); - API::lazy_refresh(); + API::dev::lazy_refresh(); } } @@ -655,12 +651,12 @@ namespace nana { drawer_->draw(); drawer_->editor()->reset_caret(); - API::lazy_refresh(); + API::dev::lazy_refresh(); } } } - void trigger::mouse_wheel(graph_reference graph, const arg_wheel& arg) + void trigger::mouse_wheel(graph_reference, const arg_wheel& arg) { if(drawer_->widget_ptr()->enabled()) { @@ -715,13 +711,13 @@ namespace nana if (call_other_keys) drawer_->editor()->respond_key(arg); - API::lazy_refresh(); + API::dev::lazy_refresh(); } - void trigger::key_char(graph_reference graph, const arg_keyboard& arg) + void trigger::key_char(graph_reference, const arg_keyboard& arg) { if (drawer_->editor()->respond_char(arg)) - API::lazy_refresh(); + API::dev::lazy_refresh(); } //end class trigger @@ -997,7 +993,7 @@ namespace nana auto editor = _m_impl().editor(); if (editor) - editor->text(to_wstring(str)); + editor->text(to_wstring(str), false); API::refresh_window(*this); } diff --git a/source/gui/widgets/date_chooser.cpp b/source/gui/widgets/date_chooser.cpp index 97150e06..0085f30b 100644 --- a/source/gui/widgets/date_chooser.cpp +++ b/source/gui/widgets/date_chooser.cpp @@ -1,7 +1,7 @@ /* * A date chooser Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -21,290 +21,410 @@ namespace nana { namespace date_chooser { - //class trigger: public drawer_trigger + enum class transform_action{ none, to_left, to_right, to_enter, to_leave }; - trigger::trigger() - : widget_(nullptr), chose_(false), page_(page::date), pos_(where::none) + void perf_transform_helper(window window_handle, transform_action tfid, trigger::graph_reference graph, trigger::graph_reference dirtybuf, trigger::graph_reference newbuf, const nana::point& refpos) + { + const int sleep_time = 15; + const int count = 20; + double delta = dirtybuf.width() / double(count); + double delta_h = dirtybuf.height() / double(count); + double fade = 1.0 / count; + + if (tfid == transform_action::to_right) { - const std::string ws[] = {"S", "M", "T", "W", "T", "F", "S"}; - for(int i = 0; i < 7; ++i) weekstr_[i] = ws[i]; + nana::rectangle dr(0, refpos.y, 0, dirtybuf.height()); + nana::rectangle nr(refpos.x, refpos.y, 0, newbuf.height()); + for (int i = 1; i < count; ++i) + { + int off_x = static_cast(delta * i); + dr.x = refpos.x + off_x; + dr.width = dirtybuf.width() - off_x; + + graph.bitblt(dr, dirtybuf); + + nr.width = off_x; + graph.bitblt(nr, newbuf, nana::point(static_cast(dr.width), 0)); + + API::update_window(window_handle); + nana::system::sleep(sleep_time); + } + } + else if (tfid == transform_action::to_left) + { + double delta = dirtybuf.width() / double(count); + nana::rectangle dr(refpos.x, refpos.y, 0, dirtybuf.height()); + nana::rectangle nr(0, refpos.y, 0, newbuf.height()); + + for (int i = 1; i < count; ++i) + { + int off_x = static_cast(delta * i); + dr.width = dirtybuf.width() - off_x; + + graph.bitblt(dr, dirtybuf, nana::point(off_x, 0)); + + nr.x = refpos.x + static_cast(dr.width); + nr.width = off_x; + graph.bitblt(nr, newbuf); + + API::update_window(window_handle); + nana::system::sleep(sleep_time); + } + } + else if (tfid == transform_action::to_leave) + { + nana::paint::graphics dzbuf(newbuf.size()); + nana::paint::graphics nzbuf(newbuf.size()); + + nana::rectangle r; + for (int i = 1; i < count; ++i) + { + r.width = static_cast(newbuf.width() - delta * i); + r.height = static_cast(newbuf.height() - delta_h * i); + r.x = static_cast(newbuf.width() - r.width) / 2; + r.y = static_cast(newbuf.height() - r.height) / 2; + + dzbuf.rectangle(true, colors::white); + dirtybuf.stretch(dzbuf, r); + + r.width = static_cast(newbuf.width() + delta * (count - i)); + r.height = static_cast(newbuf.height() + delta_h * (count - i)); + r.x = static_cast(newbuf.width() - r.width) / 2; + r.y = static_cast(newbuf.height() - r.height) / 2; + newbuf.stretch(nzbuf, r); + + nzbuf.blend(::nana::rectangle{ nzbuf.size() }, dzbuf, nana::point(), fade * (count - i)); + graph.bitblt(refpos.x, refpos.y, dzbuf); + + API::update_window(window_handle); + nana::system::sleep(sleep_time); + } + } + else if (tfid == transform_action::to_enter) + { + nana::paint::graphics dzbuf(newbuf.size()); + nana::paint::graphics nzbuf(newbuf.size()); + + nana::rectangle r; + for (int i = 1; i < count; ++i) + { + r.width = static_cast(newbuf.width() + delta * i); + r.height = static_cast(newbuf.height() + delta_h * i); + r.x = static_cast(newbuf.width() - r.width) / 2; + r.y = static_cast(newbuf.height() - r.height) / 2; + dirtybuf.stretch(dzbuf, r); + + r.width = static_cast(newbuf.width() - delta * (count - i)); + r.height = static_cast(newbuf.height() - delta_h * (count - i)); + r.x = static_cast(newbuf.width() - r.width) / 2; + r.y = static_cast(newbuf.height() - r.height) / 2; + nzbuf.rectangle(true, colors::white); + newbuf.stretch(nzbuf, r); + + nzbuf.blend(::nana::rectangle{ nzbuf.size() }, dzbuf, nana::point(), fade * (count - i)); + graph.bitblt(refpos.x, refpos.y, dzbuf); + + API::update_window(window_handle); + nana::system::sleep(sleep_time); + } + } + + graph.bitblt(nana::rectangle(refpos, newbuf.size()), newbuf); + } + + + class trigger::model + { + friend class trigger; + public: + struct view_month_rep + { + int year; + int month; + }; + + enum class where + { + none, + left_button, + right_button, + topbar, + textarea + }; + + enum class page_mode + { + date, + month + }; + + struct drawing_basis + { + nana::point refpos; + double line_s; + double row_s; + }; + + model() + { + const std::string ws[] = { "S", "M", "T", "W", "T", "F", "S" }; + for (int i = 0; i < 7; ++i) + weekstr_[i] = ws[i]; nana::date d; - chdate_.year = chmonth_.year = d.read().year; - chdate_.month = chmonth_.month = d.read().month; - chdate_.day = d.read().day; - - color_.selected = { 0x2F, 0x36, 0x99 }; - color_.highlight = { 0x4D, 0x56, 0xC8 }; - color_.normal = colors::black; - color_.bgcolor = { 0x88, 0xC4, 0xFF }; + date_.year = view_month_.year = d.read().year; + date_.month = view_month_.month = d.read().month; + date_.day = d.read().day; } - bool trigger::chose() const + void move(bool forward, graph_reference graph, window window_handle) { - return chose_; - } - - nana::date trigger::read() const - { - return nana::date(chdate_.year, chdate_.month, chdate_.day); - } - - void trigger::week_name(unsigned index, const std::string& str) - { - throw_not_utf8(str); - if(index < 7) - this->weekstr_[index] = str; - } - - trigger::where trigger::_m_pos_where(graph_reference graph, const ::nana::point& pos) - { - int xend = static_cast(graph.width()) - 1; - int yend = static_cast(graph.height()) - 1; - if(0 < pos.y && pos.y < static_cast(topbar_height)) + if (page_mode::date == page) { - if(static_cast(border_size) < pos.x && pos.x < xend) + step_view_month(forward); + + } + else + { + if (forward) + view_month_.year++; + else + view_month_.year--; + } + + perf_transform(graph, window_handle, (forward ? transform_action::to_left : transform_action::to_right)); + } + + void enter(graph_reference graph, window window_handle) + { + transform_action tfid; + + if (model::page_mode::date == page) + { + page = model::page_mode::month; + tfid = transform_action::to_leave; + + if ((!trace_.is_by_mouse) && (date_.year != view_month_.year)) { - if(pos.x < border_size + 16) + trace_.logic_pos.x = 0; + trace_.logic_pos.y = 0; + } + } + else + { + page = model::page_mode::date; + tfid = transform_action::to_enter; + + if (!trace_.is_by_mouse) + { + if ((date_.year != view_month_.year) || (date_.month != view_month_.month)) + { + trace_.logic_pos.x = 0; + trace_.logic_pos.y = 1; + } + } + } + + perf_transform(graph, window_handle, tfid); + } + + bool respond_key(graph_reference graph, const arg_keyboard& arg, bool& redrawn) + { + redrawn = false; + if (trace_.empty_logic_pos()) + return false; + + unsigned right = 7; + unsigned top = 1; + unsigned bottom = 7; + if (page_mode::month == page) + { + right = 4; + top = 0; + bottom = 3; + } + + switch (arg.key) + { + case keyboard::os_arrow_left: + if (trace_.logic_pos.x > 0) + { + --trace_.logic_pos.x; + } + else if (trace_.logic_pos.y > top) + { + trace_.logic_pos.x = right - 1; + --trace_.logic_pos.y; + } + else + { + move(false, graph, arg.window_handle); + redrawn = true; + } + break; + case keyboard::os_arrow_right: + ++trace_.logic_pos.x; + if (trace_.logic_pos.x == right) + { + if (trace_.logic_pos.y + 1 < bottom) + { + ++trace_.logic_pos.y; + trace_.logic_pos.x = 0; + } + else + { + trace_.logic_pos.x = right - 1; + move(true, graph, arg.window_handle); + redrawn = true; + } + } + + break; + case keyboard::os_arrow_up: + if (trace_.logic_pos.y == top) + trace_.logic_pos.y = bottom - 1; + else + --trace_.logic_pos.y; + break; + case keyboard::os_arrow_down: + if (trace_.logic_pos.y + 1 == bottom) + trace_.logic_pos.y = top; + else + ++trace_.logic_pos.y; + break; + case keyboard::enter: + if (page_mode::month == page) + { + int n = get_trace_logic_pos(); + view_month_.month = n; + this->enter(graph, arg.window_handle); + redrawn = true; + } + else + { + if (!trace_.empty_logic_pos()) + { + auto value = get_trace_logic_pos(); + + if (0 < value && value <= static_cast(::nana::date::month_days(view_month_.year, view_month_.month))) + this->choose(arg.window_handle, value); + else + { + move(value > 0, graph, arg.window_handle); + redrawn = true; + } + } + } + break; + case keyboard::escape: + if (page_mode::date == page) + { + enter(graph, arg.window_handle); + redrawn = true; + } + break; + default: + return false; + } + + trace_.is_by_mouse = false; + return true; + } + + ::nana::date read() const + { + return{date_.year, date_.month, date_.day}; + } + + void weekname(std::size_t pos, std::string&& name) + { + if (pos < 7) + weekstr_[pos] = std::move(name); + } + + where pos_where(const ::nana::size& area, const ::nana::point& pos) + { + int pos_right = static_cast(area.width) - 1; + int pos_bottom = static_cast(area.height) - 1; + if (0 < pos.y && pos.y < static_cast(topbar_height)) + { + if (static_cast(border_size) < pos.x && pos.x < pos_right) + { + if (pos.x < border_size + 16) return where::left_button; - else if(xend - border_size - 16 < pos.x) + else if (pos_right - border_size - 16 < pos.x) return where::right_button; return where::topbar; } } - else if(topbar_height < pos.y && pos.y < yend) + else if (topbar_height < pos.y && pos.y < pos_bottom) { - trace_pos_ = pos; + trace_.ms_pos = pos; + trace_.clear_logic_pos(); + trace_.is_by_mouse = true; + return where::textarea; } return where::none; } - void trigger::_m_draw_topbar(graph_reference graph) + bool set_where(where pos) { - ::nana::color arrow_bgcolor; - ::nana::rectangle arrow_r{ static_cast(border_size), (topbar_height - 16) / 2 + 1, 16, 16 }; - facade arrow("solid_triangle"); - arrow.direction(::nana::direction::west); - arrow.draw(graph, arrow_bgcolor, (pos_ == where::left_button ? color_.highlight : color_.normal), arrow_r, element_state::normal); + if (pos == pos_) + return false; - arrow_r.x = static_cast(graph.width()) - static_cast(border_size + 17); - arrow.direction(::nana::direction::east); - arrow.draw(graph, arrow_bgcolor, (pos_ == where::right_button ? color_.highlight : color_.normal), arrow_r, element_state::normal); - - const char * monthstr[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; - - if(graph.width() > 32 + border_size * 2) + if (pos != where::textarea) { - std::string str; - if(page_ == page::date) - { - str = ::nana::internationalization()(monthstr[chmonth_.month - 1]); - str += " "; - } - str += std::to_string(chmonth_.year); - - nana::size txt_s = graph.text_extent_size(str); - - int top = (topbar_height - static_cast(txt_s.height)) / 2 + 1; - - int xpos = static_cast(graph.width() - txt_s.width) / 2; - if(xpos < border_size + 16) xpos = 16 + border_size + 1; - - graph.string({ xpos, top }, str, (pos_ == where::topbar ? color_.highlight : color_.normal)); + trace_.clear_logic_pos(); + trace_.is_by_mouse = false; } + + pos_ = pos; + return true; } - void trigger::_m_make_drawing_basis(drawing_basis& dbasis, graph_reference graph, const nana::point& refpos) + int get_trace_logic_pos() const { - dbasis.refpos = refpos; - const unsigned width = graph.width(); - const unsigned height = graph.height(); + if (trace_.empty_logic_pos()) + return 0; - if(page::date == page_) - { - dbasis.line_s = height / 7.0; - dbasis.row_s = width / 7.0; - } - else if(page::month == page_) - { - dbasis.line_s = height / 3.0; - dbasis.row_s = width / 4.0; - } + const int rows = (page_mode::month == page ? 4 : 7); - dbasis_ = dbasis; + int n = trace_.logic_pos.y * rows + trace_.logic_pos.x + 1; + if (page_mode::date == page) + { + if (n < 8) return false; //Here is week title bar + int dw = nana::date::day_of_week(view_month_.year, view_month_.month, 1); + n -= (dw ? dw + 7 : 14); + } + return n; } - void trigger::_m_draw_pos(drawing_basis & dbasis, graph_reference graph, int x, int y, const std::string& str_utf8, bool primary, bool sel) + bool get_trace(point pos, int & res) const { - nana::rectangle r(static_cast(x * dbasis.row_s), static_cast(y * dbasis.line_s), - static_cast(dbasis.row_s), static_cast(dbasis.line_s)); + pos -= dwbasis_.refpos; - auto color = color_.normal; - auto tpos = trace_pos_ - dbasis.refpos; + int lines = 7, rows = 7; //for page::date - if((pos_ == where::textarea) - && (r.x <= tpos.x) - && (tpos.x < r.x + static_cast(r.width)) - && (r.y <= tpos.y) - && (tpos.y < r.y + static_cast(r.height))) - { - if((page_ != page::date) || y) - { - color = color_.highlight; - graph.rectangle(r, true, color_.bgcolor); - } - } - - if(sel) - { - color = color_.highlight; - graph.rectangle(r, true, color_.bgcolor); - graph.rectangle(r, false, color_.selected); - } - - if(false == primary) - color = { 0xB0, 0xB0, 0xB0 }; - - auto txt_s = graph.text_extent_size(str_utf8); - graph.string({ r.x + static_cast(r.width - txt_s.width) / 2, r.y + static_cast(r.height - txt_s.height) / 2 }, str_utf8, color); - } - - void trigger::_m_draw_pos(drawing_basis & dbasis, graph_reference graph, int x, int y, int number, bool primary, bool sel) - { - _m_draw_pos(dbasis, graph, x, y, std::to_string(number), primary, sel); - } - - void trigger::_m_draw_ex_days(drawing_basis & dbasis, graph_reference graph, int begx, int begy, bool before) - { - int x = nana::date::day_of_week(chmonth_.year, chmonth_.month, 1); - int y = (x ? 1 : 2); - - if(before) - { - int year = chmonth_.year; - int month = chmonth_.month - 1; - if(month == 0) - { - --year; - month = 12; - } - bool same = (chdate_.year == year && chdate_.month == month); - int days = nana::date::month_days(year, month); - - int size = (x ? x : 7); - int beg = days - size + 1; - - for(int i = 0; i < size; ++i) - { - this->_m_draw_pos(dbasis, graph, i, 1, beg + i, false, same && (chdate_.day == beg + i)); - } - } - else - { - int year = chmonth_.year; - int month = chmonth_.month + 1; - if(month == 13) - { - ++year; - month = 1; - } - bool same = (chdate_.year == year && chdate_.month == month); - - int day = 1; - x = begx; - for(y = begy; y < 7; ++y) - { - for(; x < 7; ++x) - { - _m_draw_pos(dbasis, graph, x, y, day, false, same && (chdate_.day == day)); - ++day; - } - x = 0; - } - } - } - - void trigger::_m_draw_days(const nana::point& refpos, graph_reference graph) - { - drawing_basis dbasis; - _m_make_drawing_basis(dbasis, graph, refpos); - - for(int i = 0; i < 7; ++i) - _m_draw_pos(dbasis, graph, i, 0, weekstr_[i], true, false); - - int day = 1; - int x = nana::date::day_of_week(chmonth_.year, chmonth_.month, 1); - int y = (x ? 1 : 2); - - //draw the days that before the first day of this month - _m_draw_ex_days(dbasis, graph, 0, 0, true); - // - int days = static_cast(nana::date::month_days(chmonth_.year, chmonth_.month)); - - bool same = (chdate_.year == chmonth_.year && chdate_.month == chmonth_.month); - while(day <= days) - { - for(; x < 7; ++x) - { - _m_draw_pos(dbasis, graph, x, y, day, true, (same && chdate_.day == day)); - if(++day > days) break; - } - if(day > days) break; - y++; - x = 0; - } - - ++x; - if(x >= 7) - { - x = 0; - ++y; - } - - _m_draw_ex_days(dbasis, graph, x, y, false); - } - - void trigger::_m_draw_months(const nana::point& refpos, graph_reference graph) - { - drawing_basis dbasis; - _m_make_drawing_basis(dbasis, graph, refpos); - - const char * monthstr[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; - ::nana::internationalization i18n; - for(int y = 0; y < 3; ++y) - for(int x = 0; x < 4; ++x) - { - int index = x + y * 4; - _m_draw_pos(dbasis, graph, x, y, i18n(monthstr[index]), true, (chmonth_.year == chdate_.year) && (index + 1 == chdate_.month)); - } - } - - bool trigger::_m_get_trace(point pos, int & res) - { - pos -= dbasis_.refpos; - - int lines = 7, rows = 7; //defaultly for page::date - - if(page_ == page::month) + if (page_mode::month == page) { lines = 3; rows = 4; } - int width = static_cast(dbasis_.row_s * rows); - int height = static_cast(dbasis_.line_s * lines); + int width = static_cast(dwbasis_.row_s * rows); + int height = static_cast(dwbasis_.line_s * lines); - if(0 <= pos.x && pos.x < width && 0 <= pos.y && pos.y < height) + if (0 <= pos.x && pos.x < width && 0 <= pos.y && pos.y < height) { - pos.x = static_cast(pos.x / dbasis_.row_s); - pos.y = static_cast(pos.y / dbasis_.line_s); + pos.x = static_cast(pos.x / dwbasis_.row_s); + pos.y = static_cast(pos.y / dwbasis_.line_s); int n = pos.y * rows + pos.x + 1; - if(page_ == page::date) + if (page_mode::date == page) { - if(n < 8) return false; //Here is week title bar - int dw = nana::date::day_of_week(chmonth_.year, chmonth_.month, 1); + if (n < 8) return false; //Here is week title bar + int dw = nana::date::day_of_week(view_month_.year, view_month_.month, 1); n -= (dw ? dw + 7 : 14); } res = n; @@ -313,294 +433,468 @@ namespace nana return false; } - void trigger::_m_perf_transform(transform_action tfid, graph_reference graph, graph_reference dirtybuf, graph_reference newbuf, const nana::point& refpos) + int days_of_view_month() const { - const int sleep_time = 15; - const int count = 20; - double delta = dirtybuf.width() / double(count); - double delta_h = dirtybuf.height() / double(count); - double fade = 1.0 / count; - - if(tfid == transform_action::to_right) - { - nana::rectangle dr(0, refpos.y, 0, dirtybuf.height()); - nana::rectangle nr(refpos.x, refpos.y, 0, newbuf.height()); - for(int i = 1; i < count; ++i) - { - int off_x = static_cast(delta * i); - dr.x = refpos.x + off_x; - dr.width = dirtybuf.width() - off_x; - - graph.bitblt(dr, dirtybuf); - - nr.width = off_x; - graph.bitblt(nr, newbuf, nana::point(static_cast(dr.width), 0)); - - API::update_window(*widget_); - nana::system::sleep(sleep_time); - } - } - else if(tfid == transform_action::to_left) - { - double delta = dirtybuf.width() / double(count); - nana::rectangle dr(refpos.x, refpos.y, 0, dirtybuf.height()); - nana::rectangle nr(0, refpos.y, 0, newbuf.height()); - - for(int i = 1; i < count; ++i) - { - int off_x = static_cast(delta * i); - dr.width = dirtybuf.width() - off_x; - - graph.bitblt(dr, dirtybuf, nana::point(off_x, 0)); - - nr.x = refpos.x + static_cast(dr.width); - nr.width = off_x; - graph.bitblt(nr, newbuf); - - API::update_window(*widget_); - nana::system::sleep(sleep_time); - } - } - else if(tfid == transform_action::to_leave) - { - nana::paint::graphics dzbuf(newbuf.size()); - nana::paint::graphics nzbuf(newbuf.size()); - - nana::rectangle r; - for(int i = 1; i < count; ++i) - { - r.width = static_cast(newbuf.width() - delta * i); - r.height = static_cast(newbuf.height() - delta_h * i); - r.x = static_cast(newbuf.width() - r.width) / 2; - r.y = static_cast(newbuf.height() - r.height) / 2; - - dzbuf.rectangle(true, colors::white); - dirtybuf.stretch(dzbuf, r); - - r.width = static_cast(newbuf.width() + delta * (count - i)); - r.height = static_cast(newbuf.height() + delta_h * (count - i)); - r.x = static_cast(newbuf.width() - r.width) / 2; - r.y = static_cast(newbuf.height() - r.height) / 2; - newbuf.stretch(nzbuf, r); - - nzbuf.blend(::nana::rectangle{ nzbuf.size() }, dzbuf, nana::point(), fade * (count - i)); - graph.bitblt(refpos.x, refpos.y, dzbuf); - - API::update_window(*widget_); - nana::system::sleep(sleep_time); - } - } - else if(tfid == transform_action::to_enter) - { - nana::paint::graphics dzbuf(newbuf.size()); - nana::paint::graphics nzbuf(newbuf.size()); - - nana::rectangle r; - for(int i = 1; i < count; ++i) - { - r.width = static_cast(newbuf.width() + delta * i); - r.height = static_cast(newbuf.height() + delta_h * i); - r.x = static_cast(newbuf.width() - r.width) / 2; - r.y = static_cast(newbuf.height() - r.height) / 2; - dirtybuf.stretch(dzbuf, r); - - r.width = static_cast(newbuf.width() - delta * (count - i)); - r.height = static_cast(newbuf.height() - delta_h * (count - i)); - r.x = static_cast(newbuf.width() - r.width) / 2; - r.y = static_cast(newbuf.height() - r.height) / 2; - nzbuf.rectangle(true, colors::white); - newbuf.stretch(nzbuf, r); - - nzbuf.blend(::nana::rectangle{ nzbuf.size() }, dzbuf, nana::point(), fade * (count - i)); - graph.bitblt(refpos.x, refpos.y, dzbuf); - - API::update_window(*widget_); - nana::system::sleep(sleep_time); - } - } - - graph.bitblt(nana::rectangle(refpos, newbuf.size()), newbuf); + return ::nana::date::month_days(view_month_.year, view_month_.month); } - void trigger::refresh(graph_reference graph) + void step_view_month(bool is_forward) + { + if (is_forward) + { + if (++view_month_.month == 13) + { + view_month_.month = 1; + ++view_month_.year; + } + } + else if (--view_month_.month == 0) + { + view_month_.month = 12; + --view_month_.year; + } + } + + view_month_rep& view_month() + { + return view_month_; + } + + + void set_view_month(int month) + { + view_month_.month = month; + } + + void choose(window window_handle, int day) + { + date_.year = view_month_.year; + date_.month = view_month_.month; + date_.day = day; + chose_ = true; + + arg_datechooser evt_arg{ static_cast(API::get_widget(window_handle)) }; + API::events(window_handle).date_changed.emit(evt_arg, window_handle); + } + + bool chose() const + { + return chose_; + } + + void render(graph_reference graph) { const unsigned width = graph.width() - 2; - graph.rectangle(false, { 0xb0, 0xb0, 0xb0 }); + graph.rectangle(false, static_cast(0xb0b0b0)); graph.rectangle({ 1, 1, width, static_cast(topbar_height) }, true, colors::white); _m_draw_topbar(graph); if (graph.height() > 2 + topbar_height) { - nana::point refpos(1, static_cast(topbar_height)+1); + const ::nana::point refpos(1, static_cast(topbar_height)+1); + + _m_calc_basis(graph, refpos); nana::paint::graphics gbuf({ width, graph.height() - 2 - topbar_height }); gbuf.rectangle(true, { 0xf0, 0xf0, 0xf0 }); - switch (page_) + switch (page) { - case page::date: - _m_draw_days(refpos, gbuf); + case page_mode::date: + _m_draw_days(gbuf); break; - case page::month: - _m_draw_months(refpos, gbuf); + case page_mode::month: + _m_draw_months(gbuf); break; default: break; } - graph.bitblt(refpos.x, refpos.y, gbuf); } } - void trigger::attached(widget_reference widget, graph_reference) + void perf_transform(graph_reference graph, window window_handle, transform_action transf) { - widget_ = &widget; + nana::point refpos(1, static_cast(topbar_height)+1); + nana::rectangle r(0, 0, graph.width() - 2, graph.height() - 2 - topbar_height); + + nana::paint::graphics dirtybuf({ r.width, r.height }); + dirtybuf.bitblt(r, graph, refpos); + + render(graph); + + nana::paint::graphics gbuf({ r.width, r.height }); + gbuf.bitblt(r, graph, refpos); + + perf_transform_helper(window_handle, transf, graph, dirtybuf, gbuf, refpos); + } + private: + //renderring functions + + void _m_calc_basis(graph_reference graph, const nana::point& refpos) + { + dwbasis_.refpos = refpos; + unsigned width = graph.width() - 2; + unsigned height = graph.height() - 2; + + if (height < topbar_height) + height = 0; + else + height -= topbar_height; + + if (page_mode::date == page) + { + dwbasis_.line_s = height / 7.0; + dwbasis_.row_s = width / 7.0; + } + else if (page_mode::month == page) + { + dwbasis_.line_s = height / 3.0; + dwbasis_.row_s = width / 4.0; + } + } + + void _m_draw_topbar(graph_reference graph) + { + ::nana::color arrow_bgcolor; + ::nana::rectangle arrow_r{ static_cast(border_size), (topbar_height - 16) / 2 + 1, 16, 16 }; + facade arrow("solid_triangle"); + arrow.direction(::nana::direction::west); + arrow.draw(graph, arrow_bgcolor, (pos_ == where::left_button ? colors_.highlighted : colors_.normal), arrow_r, element_state::normal); + + arrow_r.x = static_cast(graph.width()) - static_cast(border_size + 17); + arrow.direction(::nana::direction::east); + arrow.draw(graph, arrow_bgcolor, (pos_ == where::right_button ? colors_.highlighted : colors_.normal), arrow_r, element_state::normal); + + const char * monthstr[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; + + if (graph.width() > 32 + border_size * 2) + { + std::string str; + if (page_mode::date == page) + { + str = ::nana::internationalization()(monthstr[this->view_month_.month - 1]); + str += " "; + } + str += std::to_string(view_month_.year); + + nana::size txt_s = graph.text_extent_size(str); + + int top = (topbar_height - static_cast(txt_s.height)) / 2 + 1; + + int xpos = static_cast(graph.width() - txt_s.width) / 2; + if (xpos < border_size + 16) xpos = 16 + border_size + 1; + + graph.string({ xpos, top }, str, (pos_ == where::topbar ? colors_.highlighted : colors_.normal)); + } + } + + void _m_draw_pos(graph_reference graph, const upoint& logic_pos, const std::string& text_utf8, bool primary, bool sel) + { + nana::rectangle r(static_cast(logic_pos.x * dwbasis_.row_s), static_cast(logic_pos.y * dwbasis_.line_s), + static_cast(dwbasis_.row_s), static_cast(dwbasis_.line_s)); + + auto color = colors_.normal; + + if (trace_.is_by_mouse ? ((pos_ == where::textarea) && r.is_hit(trace_.ms_pos - dwbasis_.refpos)) : (trace_.logic_pos == logic_pos)) + { + if ((page != page_mode::date) || logic_pos.y) + { + color = colors_.highlighted; + graph.rectangle(r, true, colors_.bgcolor); + + if (trace_.is_by_mouse) + trace_.logic_pos = logic_pos; + } + } + + if (sel) + { + color = colors_.highlighted; + graph.rectangle(r, true, colors_.bgcolor); + graph.rectangle(r, false, colors_.selected); + + if (trace_.empty_logic_pos()) + trace_.logic_pos = logic_pos; + } + + if (false == primary) + color = { 0xB0, 0xB0, 0xB0 }; + + auto txt_s = graph.text_extent_size(text_utf8); + graph.string({ r.x + static_cast(r.width - txt_s.width) / 2, r.y + static_cast(r.height - txt_s.height) / 2 }, text_utf8, color); + } + + void _m_draw_ex_days(graph_reference graph, const upoint& begin_logic_pos, bool before) + { + int x = nana::date::day_of_week(view_month_.year, view_month_.month, 1); + int year = view_month_.year; + if (before) + { + + int month = view_month_.month - 1; + if (month == 0) + { + --year; + month = 12; + } + bool same = (date_.year == year && date_.month == month); + auto days = nana::date::month_days(year, month); + + unsigned size = (x ? x : 7); + unsigned beg = days - size + 1; + + for (upoint logic_pos{0, 1}; logic_pos.x < size; ++ logic_pos.x) + { + this->_m_draw_pos(graph, logic_pos, std::to_string(beg + logic_pos.x), false, same && (static_cast(date_.day) == beg + logic_pos.x)); + } + } + else + { + int month = view_month_.month + 1; + if (month == 13) + { + ++year; + month = 1; + } + bool same = (date_.year == year && date_.month == month); + + int day = 1; + + for (upoint logic_pos{begin_logic_pos}; logic_pos.y < 7; ++logic_pos.y) + { + for (; logic_pos.x < 7; ++logic_pos.x) + { + _m_draw_pos(graph, logic_pos, std::to_string(day), false, same && (date_.day == day)); + ++day; + } + logic_pos.x = 0; + } + } + } + + void _m_draw_days(graph_reference graph) + { + upoint logic_pos{ 0, 0 }; + for (; logic_pos.x < 7; ++logic_pos.x) + _m_draw_pos(graph, logic_pos, weekstr_[logic_pos.x], true, false); + + //draw the days that before the first day of this month + _m_draw_ex_days(graph, {}, true); + + int days = static_cast(nana::date::month_days(view_month_.year, view_month_.month)); + + bool same = (date_.year == view_month_.year && date_.month == view_month_.month); + + logic_pos.x = ::nana::date::day_of_week(view_month_.year, view_month_.month, 1); + logic_pos.y = (logic_pos.x ? 1 : 2); + + int day = 1; + while (day <= days) + { + for (; logic_pos.x < 7; ++logic_pos.x) + { + _m_draw_pos(graph, logic_pos, std::to_string(day), true, (same && date_.day == day)); + if (++day > days) break; + } + if (day > days) break; + logic_pos.y++; + logic_pos.x = 0; + } + + ++logic_pos.x; + if (logic_pos.x >= 7) + { + logic_pos.x = 0; + ++logic_pos.y; + } + + _m_draw_ex_days(graph, logic_pos, false); + } + + void _m_draw_months(graph_reference graph) + { + const char * monthstr[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; + ::nana::internationalization i18n; + + for (unsigned y = 0; y < 3; ++y) + for (upoint logic_pos{0, y}; logic_pos.x < 4; ++logic_pos.x) + { + int index = logic_pos.x + logic_pos.y * 4; + _m_draw_pos(graph, logic_pos, i18n(monthstr[index]), true, (view_month_.year == date_.year) && (index + 1 == date_.month)); + } + } + private: + page_mode page{ page_mode::date }; + private: + ::std::string weekstr_[7]; + + bool chose_{ false }; //indicates whether the date is chose + where pos_{ where::none }; + + struct + { + bool is_by_mouse{ true }; + point ms_pos; //the mouse position + upoint logic_pos{ 8, 8 }; //pos(8, 8) indicates the end position. + + void clear_logic_pos() + { + logic_pos.x = logic_pos.y = 8; + } + + bool empty_logic_pos() const + { + return (logic_pos.x == 8); + } + }trace_; + + drawing_basis dwbasis_; + + struct color_rep + { + ::nana::color highlighted{ static_cast(0x4d56c8) }; + ::nana::color selected{ static_cast(0x2f3699) }; + ::nana::color normal{ colors::black }; + ::nana::color bgcolor{ static_cast(0x88c4ff) }; + }colors_; + + view_month_rep view_month_; + + struct date_mode + { + int year; + int month; + int day; + }date_; + }; + //class trigger: public drawer_trigger + + trigger::trigger() + : model_(new model) + { + } + + trigger::~trigger() + { + delete model_; + } + + auto trigger::get_model() const -> model* + { + return model_; + } + + void trigger::refresh(graph_reference graph) + { + model_->render(graph); } void trigger::mouse_move(graph_reference graph, const arg_mouse& arg) { - where pos = _m_pos_where(graph, arg.pos); - if(pos == pos_ && pos_ != where::textarea) return; - pos_ = pos; - refresh(graph); - API::lazy_refresh(); + auto pos = model_->pos_where(graph.size(), arg.pos); + if (model_->set_where(pos) || (model::where::textarea == pos)) + { + model_->render(graph); + API::dev::lazy_refresh(); + } } void trigger::mouse_leave(graph_reference graph, const arg_mouse&) { - if(where::none == pos_) return; - pos_ = where::none; - refresh(graph); - API::lazy_refresh(); + if (model_->set_where(model::where::none)) + { + model_->render(graph); + API::dev::lazy_refresh(); + } } void trigger::mouse_up(graph_reference graph, const arg_mouse& arg) { bool redraw = true; - where pos = _m_pos_where(graph, arg.pos); - transform_action tfid = transform_action::none; + auto pos = model_->pos_where(graph.size(), arg.pos); + //transform_action tfid = transform_action::none; + bool transformed = false; - if(pos == where::topbar) + if (pos == model::where::topbar) { - switch(page_) + if (model::page_mode::date == model_->page) { - case page::date: - page_ = page::month; - tfid = transform_action::to_leave; - break; - default: - redraw = false; + model_->enter(graph, arg.window_handle); + transformed = true; } + else + redraw = false; } - else if(pos == where::textarea) + else if (pos == model::where::textarea) { int ret = 0; - switch(page_) + + bool good_trace = model_->get_trace(arg.pos, ret); + + switch (model_->page) { - case page::date: - if(_m_get_trace(arg.pos, ret)) + case model::page_mode::date: + if (good_trace) { - if(ret < 1) + if (ret < 1) { - if(--chmonth_.month == 0) - { - --chmonth_.year; - chmonth_.month = 12; - } - tfid = transform_action::to_right; + model_->move(false, graph, arg.window_handle); + transformed = true; } else { - int days = nana::date::month_days(chmonth_.year, chmonth_.month); - if(ret > days) + auto days = model_->days_of_view_month(); + if (ret > days) { - if(++chmonth_.month == 13) - { - ++chmonth_.year; - chmonth_.month = 1; - } - tfid = transform_action::to_left; + model_->move(true, graph, arg.window_handle); + transformed = true; } else //Selecting a day in this month { - chdate_.year = chmonth_.year; - chdate_.month = chmonth_.month; - chdate_.day = ret; - chose_ = true; + model_->choose(arg.window_handle, ret); } } } break; - case page::month: - if(_m_get_trace(arg.pos, ret)) - chmonth_.month = ret; - page_ = page::date; - tfid = transform_action::to_enter; + case model::page_mode::month: + if (good_trace) + model_->view_month().month = ret; + + model_->enter(graph, arg.window_handle); + transformed = true; break; default: redraw = false; } } - else if(pos == where::left_button || pos == where::right_button) + else if (model::where::left_button == pos || model::where::right_button == pos) { - int end_m; - int beg_m; - int step; - if(pos == where::left_button) - { - end_m = 1; - beg_m = 12; - step = -1; - tfid = transform_action::to_right; - } - else - { - end_m = 12; - beg_m = 1; - step = 1; - tfid = transform_action::to_left; - } - switch(page_) - { - case page::date: - if(chmonth_.month == end_m) - { - chmonth_.month = beg_m; - chmonth_.year += step; - } - else - chmonth_.month += step; - break; - case page::month: - chmonth_.year += step; - break; - default: - redraw = false; - } + model_->move((model::where::right_button == pos), graph, arg.window_handle); + transformed = true; } + else + redraw = false; - if(redraw) + if (redraw) { - if(tfid != transform_action::none) - { - nana::point refpos(1, static_cast(topbar_height) + 1); - nana::rectangle r(0, 0, graph.width() - 2, graph.height() - 2 - topbar_height); + if (!transformed) + model_->render(graph); - nana::paint::graphics dirtybuf({ r.width, r.height }); - dirtybuf.bitblt(r, graph, refpos); + API::dev::lazy_refresh(); + } + } + void trigger::key_press(graph_reference graph, const arg_keyboard& arg) + { + bool redrawn = false; + if (model_->respond_key(graph, arg, redrawn)) + { + if (!redrawn) refresh(graph); - nana::paint::graphics gbuf({ r.width, r.height }); - gbuf.bitblt(r, graph, refpos); - - _m_perf_transform(tfid, graph, dirtybuf, gbuf, refpos); - } - else - refresh(graph); - - API::lazy_refresh(); + API::dev::lazy_refresh(); } } //end class trigger @@ -616,18 +910,6 @@ namespace nana create(wd, rectangle(), visible); } - date_chooser::date_chooser(window wd, const std::string& text, bool visible) - { - throw_not_utf8(text); - create(wd, rectangle(), visible); - caption(text); - } - - date_chooser::date_chooser(window wd, const char* text, bool visible) - : date_chooser(wd, std::string(text), visible) - { - } - date_chooser::date_chooser(window wd, const rectangle& r, bool visible) { create(wd, r, visible); @@ -635,17 +917,17 @@ namespace nana bool date_chooser::chose() const { - return get_drawer_trigger().chose(); + return get_drawer_trigger().get_model()->chose(); } nana::date date_chooser::read() const { - return get_drawer_trigger().read(); + return get_drawer_trigger().get_model()->read(); } - void date_chooser::weekstr(unsigned index, const ::std::string& str) + void date_chooser::weekstr(unsigned index, ::std::string str) { - get_drawer_trigger().week_name(index, str); + get_drawer_trigger().get_model()->weekname(index, std::move(str)); API::refresh_window(*this); } //end class date_chooser diff --git a/source/gui/widgets/float_listbox.cpp b/source/gui/widgets/float_listbox.cpp index d2591ac0..34c5a03f 100644 --- a/source/gui/widgets/float_listbox.cpp +++ b/source/gui/widgets/float_listbox.cpp @@ -443,7 +443,7 @@ namespace nana if(drawer_->set_mouse(graph, arg.pos.x, arg.pos.y)) { drawer_->draw(); - API::lazy_refresh(); + API::dev::lazy_refresh(); } } @@ -469,8 +469,8 @@ namespace nana float_listbox::float_listbox(window wd, const rectangle & r, bool is_ignore_first_mouse_up) :base_type(wd, false, r, appear::bald()) { - API::capture_window(handle(), true); - API::capture_ignore_children(false); + this->set_capture(false); + API::take_active(handle(), false, parent()); auto & impl = get_drawer_trigger().get_drawer_impl(); impl.clear_state(); diff --git a/source/gui/widgets/form.cpp b/source/gui/widgets/form.cpp index eab587ad..7f8247ff 100644 --- a/source/gui/widgets/form.cpp +++ b/source/gui/widgets/form.cpp @@ -18,7 +18,7 @@ namespace nana namespace form { //class trigger - void trigger::attached(widget_reference wdg, graph_reference graph) + void trigger::attached(widget_reference wdg, graph_reference) { wd_ = &wdg; API::ignore_mouse_focus(wdg, true); @@ -28,27 +28,61 @@ namespace nana { graph.rectangle(true, API::bgcolor(*wd_)); } + //end class trigger + + //class form_base + form_base::form_base(window owner, bool nested, const rectangle& r, const appearance& app) + : widget_object(owner, nested, r, app) + {} + + place & form_base::get_place() + { + if (this->empty()) + throw std::runtime_error("form::get_plac: the form has destroyed."); + + if (!place_) + place_.reset(new place{ *this }); + + return *place_; + } + + void form_base::div(const char* div_text) + { + get_place().div(div_text); + } + + place::field_reference form_base::operator[](const char* field_name) + { + return get_place()[field_name]; + } + + void form_base::collocate() noexcept + { + if (place_) + place_->collocate(); + } + //end class form_base }//end namespace form }//end namespace drawerbase //class form - typedef widget_object form_base_t; + using form_base = drawerbase::form::form_base; form::form(const form& fm, const ::nana::size& sz, const appearance& apr) - : form_base_t(fm.handle(), false, API::make_center(fm.handle(), sz.width, sz.height), apr) + : form_base(fm.handle(), false, API::make_center(fm.handle(), sz.width, sz.height), apr) { } form::form(const rectangle& r, const appearance& apr) - : form_base_t(nullptr, false, r, apr) + : form_base(nullptr, false, r, apr) {} form::form(window owner, const ::nana::size& sz, const appearance& apr) - : form_base_t(owner, false, API::make_center(owner, sz.width, sz.height), apr) + : form_base(owner, false, API::make_center(owner, sz.width, sz.height), apr) {} form::form(window owner, const rectangle& r, const appearance& apr) - : form_base_t(owner, false, r, apr) + : form_base(owner, false, r, apr) {} void form::modality() const @@ -64,21 +98,21 @@ namespace nana //class nested_form nested_form::nested_form(const form& fm, const rectangle& r, const appearance& apr) - : form_base_t(fm.handle(), true, r, apr) + : form_base(fm.handle(), true, r, apr) { } nested_form::nested_form(const nested_form& fm, const rectangle& r, const appearance& apr) - : form_base_t(fm.handle(), true, r, apr) + : form_base(fm.handle(), true, r, apr) { } nested_form::nested_form(window owner, const appearance& apr) - : form_base_t(owner, true, rectangle(), apr) + : form_base(owner, true, rectangle(), apr) {} nested_form::nested_form(window owner, const rectangle& r, const appearance& apr) - : form_base_t(owner, true, r, apr) + : form_base(owner, true, r, apr) {} //end nested_form }//end namespace nana diff --git a/source/gui/widgets/frame.cpp b/source/gui/widgets/frame.cpp index 27121345..7b9e7af2 100644 --- a/source/gui/widgets/frame.cpp +++ b/source/gui/widgets/frame.cpp @@ -13,6 +13,8 @@ #include +#ifndef WIDGET_FRAME_DEPRECATED + namespace nana { //class frame:: public widget_object @@ -45,3 +47,5 @@ namespace nana //end class frame }//end namespace nana +#endif + diff --git a/source/gui/widgets/label.cpp b/source/gui/widgets/label.cpp index ed6c2828..d500eed9 100644 --- a/source/gui/widgets/label.cpp +++ b/source/gui/widgets/label.cpp @@ -437,7 +437,7 @@ namespace nana return total_w; } - bool _m_each_line(graph_reference graph, dstream::linecontainer& line, render_status& rs) + bool _m_each_line(graph_reference graph, dstream::linecontainer&, render_status& rs) { std::wstring text; iterator block_start; diff --git a/source/gui/widgets/listbox.cpp b/source/gui/widgets/listbox.cpp index 5dc59321..85889705 100644 --- a/source/gui/widgets/listbox.cpp +++ b/source/gui/widgets/listbox.cpp @@ -13,6 +13,7 @@ * Ariel Vina-Rodriguez * leobackes(pr#86,pr#97) * Benjamin Navarro(pr#81) + * besh81(pr#130) * */ @@ -22,12 +23,15 @@ #include #include +#include + #include #include #include #include #include #include +#include namespace nana { @@ -35,6 +39,28 @@ namespace nana { namespace listbox { + class model_lock_guard + { + model_lock_guard(const model_lock_guard&) = delete; + model_lock_guard& operator=(const model_lock_guard&) = delete; + public: + model_lock_guard(model_interface* model) + : model_ptr_(model) + { + if (model_ptr_) + model_ptr_->lock(); + } + + ~model_lock_guard() + { + if (model_ptr_) + model_ptr_->unlock(); + } + private: + model_interface* const model_ptr_; + }; + + //struct cell cell::format::format(const ::nana::color& bgcolor, const ::nana::color& fgcolor) : bgcolor{ bgcolor }, fgcolor{ fgcolor } @@ -87,271 +113,159 @@ namespace nana } //end struct cell - //definition of iresolver/oresolver - oresolver& oresolver::operator<<(bool n) - { - cells_.emplace_back(n ? "true" : "false"); - return *this; - } - oresolver& oresolver::operator<<(short n) - { - cells_.emplace_back(std::to_string(n)); - return *this; - } - - oresolver& oresolver::operator<<(unsigned short n) - { - cells_.emplace_back(std::to_string(n)); - return *this; - } - - oresolver& oresolver::operator<<(int n) - { - cells_.emplace_back(std::to_string(n)); - return *this; - } - - oresolver& oresolver::operator<<(unsigned int n) - { - cells_.emplace_back(std::to_string(n)); - return *this; - } - - oresolver& oresolver::operator<<(long n) - { - cells_.emplace_back(std::to_string(n)); - return *this; - } - - oresolver& oresolver::operator<<(unsigned long n) - { - cells_.emplace_back(std::to_string(n)); - return *this; - } - oresolver& oresolver::operator<<(long long n) - { - cells_.emplace_back(std::to_string(n)); - return *this; - } - - oresolver& oresolver::operator<<(unsigned long long n) - { - cells_.emplace_back(std::to_string(n)); - return *this; - } - - oresolver& oresolver::operator<<(float f) - { - cells_.emplace_back(std::to_string(f)); - return *this; - } - - oresolver& oresolver::operator<<(double f) - { - cells_.emplace_back(std::to_string(f)); - return *this; - } - - oresolver& oresolver::operator<<(long double f) - { - cells_.emplace_back(std::to_string(f)); - return *this; - } - - oresolver& oresolver::operator<<(const char* text) - { - cells_.emplace_back(text); - return *this; - } - - oresolver& oresolver::operator<<(const wchar_t* text) - { - cells_.emplace_back(to_utf8(text)); - return *this; - } - - oresolver& oresolver::operator<<(const std::string& text) - { - cells_.emplace_back(text); - return *this; - } - - oresolver& oresolver::operator<<(const std::wstring& text) - { - cells_.emplace_back(to_utf8(text)); - return *this; - } - - oresolver& oresolver::operator<<(std::wstring&& text) - { - cells_.emplace_back(to_utf8(text)); - return *this; - } - - oresolver& oresolver::operator<<(cell cl) - { - cells_.emplace_back(std::move(cl)); - return *this; - } - - oresolver& oresolver::operator<<(std::nullptr_t) - { - cells_.emplace_back(); - cells_.back().text.assign(1, wchar_t(0)); //means invalid cell - return *this; - } - - std::vector&& oresolver::move_cells() - { - return std::move(cells_); - } - - iresolver& iresolver::operator>>(bool& n) - { - if (pos_ < cells_.size()) - n = (std::stoi(cells_[pos_++].text) == 0); - return *this; - } - - iresolver& iresolver::operator>>(short& n) - { - if (pos_ < cells_.size()) - n = std::stoi(cells_[pos_++].text); - return *this; - } - - iresolver& iresolver::operator>>(unsigned short& n) - { - if (pos_ < cells_.size()) - n = static_cast(std::stoul(cells_[pos_++].text)); - return *this; - } - - iresolver& iresolver::operator>>(int& n) - { - if (pos_ < cells_.size()) - n = std::stoi(cells_[pos_++].text); - return *this; - } - - iresolver& iresolver::operator>>(unsigned int& n) - { - if (pos_ < cells_.size()) - n = std::stoul(cells_[pos_++].text); - return *this; - } - - iresolver& iresolver::operator>>(long& n) - { - if (pos_ < cells_.size()) - n = std::stol(cells_[pos_++].text); - return *this; - } - - iresolver& iresolver::operator>>(unsigned long& n) - { - if (pos_ < cells_.size()) - n = std::stoul(cells_[pos_++].text); - return *this; - } - - iresolver& iresolver::operator>>(long long& n) - { - if (pos_ < cells_.size()) - n = std::stoll(cells_[pos_++].text); - return *this; - } - iresolver& iresolver::operator>>(unsigned long long& n) - { - if (pos_ < cells_.size()) - n = std::stoull(cells_[pos_++].text); - return *this; - } - iresolver& iresolver::operator>>(float& f) - { - if (pos_ < cells_.size()) - f = std::stof(cells_[pos_++].text); - return *this; - } - - iresolver& iresolver::operator>>(double& f) - { - if (pos_ < cells_.size()) - f = std::stod(cells_[pos_++].text); - return *this; - } - - iresolver& iresolver::operator>>(long double& f) - { - if (pos_ < cells_.size()) - f = std::stold(cells_[pos_++].text); - return *this; - } - - iresolver& iresolver::operator>>(std::string& text) - { - if (pos_ < cells_.size()) - text = cells_[pos_++].text; - return *this; - } - - iresolver& iresolver::operator>>(std::wstring& text) - { - if (pos_ < cells_.size()) - text = to_wstring(cells_[pos_++].text); - - return *this; - } - - iresolver::iresolver(const std::vector& cl) - : cells_(cl) - {} - - iresolver& iresolver::operator>>(cell& cl) - { - if (pos_ < cells_.size()) - cl = cells_[pos_++]; - return *this; - } - - iresolver& iresolver::operator>>(std::nullptr_t) - { - ++pos_; - return *this; - } - //end class iresolver/oresolver - - /// Essence of the columns Header + // Essence of the columns Header class es_header { public: - - struct column_t + struct column + : public column_interface { - native_string_type text; ///< "text" header of the column number "index" with weigth "pixels" - unsigned pixels; - bool visible{true}; + native_string_type text; + unsigned width_px; + std::pair range_width_px; + bool visible_state{ true }; + + /// Position of column when it was creating size_type index; + + nana::align alignment{ nana::align::left }; + std::function weak_ordering; - column_t() = default; - column_t(native_string_type&& txt, unsigned px, size_type pos) - : text(std::move(txt)), pixels(px), index(pos) - {} + + column() = default; + + + column(const column&) = default; + + column& operator=(const column& other) + { + if (this != &other) + { + text = other.text; + width_px = other.width_px; + range_width_px = other.range_width_px; + visible_state = other.visible_state; + index = other.index; + alignment = other.alignment; + weak_ordering = other.weak_ordering; + } + return *this; + + } + + column(column&& other): + text(std::move(other.text)), + width_px(other.width_px), + range_width_px(other.range_width_px), + visible_state(other.visible_state), + index(other.index), + alignment(other.alignment), + weak_ordering(std::move(other.weak_ordering)), + ess_(other.ess_) + { + } + + column& operator=(column&& other) + { + if (this != &other) + { + text = std::move(other.text); + width_px = other.width_px; + range_width_px = other.range_width_px; + visible_state = other.visible_state; + index = other.index; + alignment = other.alignment; + weak_ordering = std::move(other.weak_ordering); + } + return *this; + } + + column(essence* ess, native_string_type&& text, unsigned px, size_type pos) : + text(std::move(text)), + width_px(px), + index(pos), + ess_(ess) + { + } + private: + //The definition is provided after essence + void _m_refresh(); + private: + essence* const ess_; + public: + //Implementation of column_interface + unsigned width() const noexcept override + { + return width_px; + } + + // Sets the width and overrides the ranged width + void width(unsigned pixels) noexcept override + { + width_px = pixels; + range_width_px.first = range_width_px.second = 0; + + _m_refresh(); + } + + void width(unsigned minimum, unsigned maximum) + { + //maximum must be larger than minimum, but maximum == 0 is allowed if minimum is 0 + if ((minimum >= maximum) && (minimum != 0)) + throw std::invalid_argument("listbox.column.width() minimum must be less than maximum"); + + range_width_px.first = minimum; + range_width_px.second = maximum; + + if (width_px < range_width_px.first) + { + width_px = range_width_px.first; + _m_refresh(); + } + else if (range_width_px.second < width_px) + { + width_px = range_width_px.second; + _m_refresh(); + } + } + + void text_align(::nana::align align) noexcept override + { + if (alignment != align) + { + alignment = align; + _m_refresh(); + } + } + + //Definition is provided after essence + void fit_content(unsigned maximize = 100000) noexcept override; + + bool visible() const noexcept override + { + return visible_state; + } + + void visible(bool is_visible) noexcept override + { + visible_state = is_visible; + _m_refresh(); + } }; - using container = std::vector ; + using container = std::vector; - export_options::columns_indexs all_headers(bool only_visibles) const - { - export_options::columns_indexs idx; - for(const auto &header : cont()) + export_options::columns_indexs all_headers(bool only_visibles) const + { + export_options::columns_indexs idx; + for(const auto &col : cont()) { - if(header.visible || !only_visibles) - idx.push_back(header.index); + if(col.visible_state || !only_visibles) + idx.push_back(col.index); } - return idx; - } + return idx; + } std::string to_string(const export_options& exp_opt) const { @@ -363,7 +277,7 @@ namespace nana first=false; else head_str += exp_opt.sep; - head_str += to_utf8(column_ref(exp_opt.columns_order[idx]).text); + head_str += to_utf8(at(exp_opt.columns_order[idx]).text); } return head_str; } @@ -392,91 +306,157 @@ namespace nana sortable_ = enable; } - std::function fetch_comp(std::size_t pos) const + size_type create(essence* ess, native_string_type&& text, unsigned pixels) { - try - { - return column_ref(pos).weak_ordering; - } - catch (...) - { - } - return{}; - } - - size_type create(native_string_type&& text, unsigned pixels) - { - cont_.emplace_back(std::move(text), pixels, static_cast(cont_.size())); + cont_.emplace_back(ess, std::move(text), pixels, static_cast(cont_.size())); return cont_.back().index; } - void item_width(size_type pos, unsigned width) - { - column(pos).pixels = width; - } - - unsigned item_width(size_type pos) const throw() - { - try - { - return column_ref(pos).pixels; - } - catch (...) - { - } - return 0; - } - - unsigned pixels() const + unsigned pixels() const ///< the visible width of the whole header { unsigned pixels = 0; - for(auto & m : cont_) + for(auto & col : cont_) { - if(m.visible) - pixels += m.pixels; + if (col.visible_state) + pixels += col.width_px; } return pixels; } + /// Calculates the ranged columns to make the whole header fit a specified width + /** + * @param width The width to be fittd + * @return true if the ranged columns is adjusted for the width, false otherwise. + */ + bool calc_ranged_columns(unsigned width) + { + unsigned fixed_px = 0; + unsigned minimal_px = 0; + unsigned maximal_px = 0; + + unsigned ranged_px = 0; + unsigned ranged_count = 0; + + for (auto & col : cont_) + { + if (col.visible_state) + { + if (col.range_width_px.first == col.range_width_px.second) + { + fixed_px += col.width_px; + continue; + } + + minimal_px += col.range_width_px.first; + maximal_px += col.range_width_px.second; + + ranged_px += col.width_px; + ++ranged_count; + } + } + + // Don't calculate because the header fits the width + if (ranged_px + fixed_px == width) + return true; + + //Don't calculate the ranged columns if + //There isn't a ranged column while maximal_px == 0, or + //the minimal ranged size is larger than width + if ((0 == maximal_px) || (fixed_px + minimal_px > width)) + return false; + + if (ranged_px + fixed_px > width) + { + auto delta_px = ranged_px + fixed_px - width; + + while (delta_px) + { + for (auto & col : cont_) + { + if (0 == delta_px) + break; + + if (col.visible_state && (col.range_width_px.first < col.range_width_px.second)) + { + if (col.range_width_px.first < col.width_px) + { + --col.width_px; + --delta_px; + } + } + } + } + } + else + { + auto delta_px = width - (ranged_px + fixed_px); + while (delta_px) + { + for (auto & col : cont_) + { + if (0 == delta_px) + break; + + if (col.visible_state && (col.range_width_px.first < col.range_width_px.second)) + { + if (col.width_px < col.range_width_px.second) + { + ++col.width_px; + --delta_px; + } + } + } + } + } + + return true; + } + const container& cont() const { return cont_; } /// find and return a ref to the column that originaly was at position "pos" previous to any list reorganization. - column_t& column(size_type pos) + column& at(size_type pos) { for(auto & m : cont_) { - if(m.index == pos) + if (m.index == pos) return m; } throw std::out_of_range("Nana.GUI.Listbox: invalid header index."); } - const column_t& column_ref(size_type pos) const + + const column& at(size_type pos) const { for(const auto & m : cont_) { - if(m.index == pos) + if (m.index == pos) return m; } throw std::out_of_range("Nana.GUI.Listbox: invalid header index."); } - /// return the original index of the current column at x . - size_type item_by_x(int x) const + /// Returns the position(original index when it is creating) of the current column at point x + size_type column_from_point(int x) const { - for(const auto & col : cont_) // in current order + for (const auto & col : cont_) { - if(x < static_cast(col.pixels)) - return col.index; - x -= col.pixels; + if (col.visible_state) + { + if (x < static_cast(col.width_px)) + return col.index; + + x -= static_cast(col.width_px); + continue; + } } return npos; } - /// return the left position of the column originaly at index "pos" . - int item_pos(size_type pos, unsigned * pixels) const + /// Returns the left point position and width(in variable *pixels) of column originaly at position pos. + int position(size_type pos, unsigned * pixels) const { int left = 0; for (auto & m : cont_) @@ -484,16 +464,17 @@ namespace nana if (m.index == pos) { if (pixels) - *pixels = m.pixels; + *pixels = m.width_px; break; } - if (m.visible) - left += m.pixels; + if (m.visible_state) + left += m.width_px; } return left; } - /// 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 originaly at index "index" size_type neighbor(size_type index, bool front) const { size_type n = npos; @@ -504,22 +485,22 @@ namespace nana if(front) return n; for(++i; i != cont_.cend(); ++i) { - if(i->visible) return i->index; + if(i->visible_state) return i->index; } break; } - else if(i->visible) - n = i->index; + else if(i->visible_state) + n = i->index; } return npos; } - /// return the original index of the currently first visible col + + /// return the original index of the currently first visible col size_type begin() const { for(const auto & m : cont_) - { - if(m.visible) return m.index; - } + if(m.visible_state) return m.index; + return npos; } @@ -528,11 +509,13 @@ namespace nana { for(auto i = cont_.rbegin(); i != cont_.rend(); ++i) { - if(i->visible) return i->index; + if(i->visible_state) + return i->index; } return npos; } - /// move the col originaly at index to the position currently in front (or after) the col originaly at index "to" invalidating some current index + + /// move the col originaly at "index" to the position currently in front (or after) the col originaly at index "to" invalidating some current index void move(size_type index, size_type to, bool front) throw() { if ((index == to) || (index >= cont_.size()) || (to >= cont_.size())) @@ -542,14 +525,14 @@ namespace nana { if (index == i->index) { - column_t from = std::move(*i); + auto col_from = std::move(*i); cont_.erase(i); for (auto u = cont_.begin(); u != cont_.end(); ++u) { if (to == u->index) { - cont_.insert(front ? u : ++u, from); + cont_.insert(front ? u : ++u, col_from); return; } } @@ -563,13 +546,12 @@ namespace nana container cont_; }; - struct essence_t; - struct item_t + struct item_data { using container = std::vector; - container cells; + std::unique_ptr cells; nana::color bgcolor; nana::color fgcolor; paint::image img; @@ -583,13 +565,13 @@ namespace nana mutable std::unique_ptr anyobj; - item_t() + item_data() { flags.selected = flags.checked = false; } - item_t(const item_t& r) - : cells(r.cells), + item_data(const item_data& r) + : cells(r.cells ? std::make_unique(*r.cells) : nullptr), bgcolor(r.bgcolor), fgcolor(r.fgcolor), img(r.img), @@ -597,31 +579,35 @@ namespace nana anyobj(r.anyobj ? new nana::any(*r.anyobj) : nullptr) {} - item_t(container&& cont) - : cells(std::move(cont)) + item_data(container&& cont) + : cells(std::make_unique(std::move(cont))) { flags.selected = flags.checked = false; } - item_t(std::string&& s) + item_data(std::string&& s) + : cells(std::make_unique()) { flags.selected = flags.checked = false; - cells.emplace_back(std::move(s)); + cells->emplace_back(std::move(s)); } - item_t(std::string&& s, const nana::color& bg, const nana::color& fg) - : bgcolor(bg), - fgcolor(fg) + item_data(std::string&& s, const nana::color& bg, const nana::color& fg): + cells(std::make_unique()), + bgcolor(bg), + fgcolor(fg) { flags.selected = flags.checked = false; - cells.emplace_back(std::move(s)); + cells->emplace_back(std::move(s)); } - item_t& operator=(const item_t& r) + item_data& operator=(const item_data& r) { if (this != &r) { - cells = r.cells; + if (r.cells) + cells = std::make_unique(*r.cells); + flags = r.flags; anyobj.reset(r.anyobj ? new nana::any(*r.anyobj) : nullptr); bgcolor = r.bgcolor; @@ -631,19 +617,24 @@ namespace nana return *this; } - std::string to_string(const export_options& exp_opt) const + std::string to_string(const export_options& exp_opt, const std::vector* model_cells) const { std::string item_str; bool ignore_first = true; - for (size_type idx{}; idx < exp_opt.columns_order.size(); ++idx) + + for (auto col : exp_opt.columns_order) { if (ignore_first) ignore_first = false; else item_str += exp_opt.sep; - item_str += cells[exp_opt.columns_order[idx]].text; + //Use the model cells instead if model cells is avaiable + if (model_cells) + item_str += model_cells->operator[](col).text; + else + item_str += (*cells)[col].text; } return item_str; @@ -654,11 +645,14 @@ namespace nana struct category_t { - using container = std::deque; + using container = std::deque; native_string_type text; std::vector sorted; container items; + + std::unique_ptr model_ptr; + bool expand{true}; //A cat may have a key object to identify the category @@ -677,7 +671,8 @@ namespace nana { for (auto & m : items) { - if (m.flags.selected == false) return false; + if (false == m.flags.selected) + return false; } return !items.empty(); } @@ -688,16 +683,18 @@ namespace nana public: using container = std::list; + using item_type = item_data; + std::function(std::size_t) > fetch_ordering_comparer; es_lister() { //#0 is a default category - list_.emplace_back(); + categories_.emplace_back(); } - void bind(essence_t* ess, widget& wd) + void bind(essence* ess, widget& wd) { ess_ = ess; widget_ = dynamic_cast(&wd); @@ -720,7 +717,7 @@ namespace nana if (allocate_if_empty) { - item.anyobj.reset(new nana::any); //make_unique + item.anyobj.reset(new any); return item.anyobj.get(); } } @@ -729,6 +726,10 @@ namespace nana std::string to_string(const export_options& exp_opt) const; + + // Definition is provided after struct essence + unsigned column_content_pixels(size_type pos) const; + /// each sort() ivalidate any existing reference from display position to absolute item, that is after sort() display offset point to different items void sort() { @@ -738,61 +739,123 @@ namespace nana auto weak_ordering_comp = fetch_ordering_comparer(sorted_index_); if(weak_ordering_comp) { - for(auto & cat: list_) + for (auto & cat : categories_) { auto bi = std::begin(cat.sorted); auto ei = std::end(cat.sorted); - std::sort(bi, ei, [&cat, &weak_ordering_comp, this](std::size_t x, std::size_t y){ + + if (cat.model_ptr) + { + std::sort(bi, ei, [&cat, &weak_ordering_comp, this](std::size_t x, std::size_t y){ //The predicate must be a strict weak ordering. //!comp(x, y) != comp(x, y) auto & mx = cat.items[x]; auto & my = cat.items[y]; - if (mx.cells.size() <= sorted_index_ || my.cells.size() <= sorted_index_) + + auto mx_cells = cat.model_ptr->container()->to_cells(x); + auto my_cells = cat.model_ptr->container()->to_cells(y); + + if (mx_cells.size() <= sorted_index_ || my_cells.size() <= sorted_index_) { std::string a; - if (mx.cells.size() > sorted_index_) - a = mx.cells[sorted_index_].text; + if (mx_cells.size() > sorted_index_) + a = mx_cells[sorted_index_].text; std::string b; - if (my.cells.size() > sorted_index_) - b = my.cells[sorted_index_].text; + if (my_cells.size() > sorted_index_) + b = my_cells[sorted_index_].text; return weak_ordering_comp(a, mx.anyobj.get(), b, my.anyobj.get(), sorted_reverse_); } - return weak_ordering_comp(mx.cells[sorted_index_].text, mx.anyobj.get(), my.cells[sorted_index_].text, my.anyobj.get(), sorted_reverse_); + return weak_ordering_comp(mx_cells[sorted_index_].text, mx.anyobj.get(), my_cells[sorted_index_].text, my.anyobj.get(), sorted_reverse_); }); + } + else + { + std::sort(bi, ei, [&cat, &weak_ordering_comp, this](std::size_t x, std::size_t y){ + //The predicate must be a strict weak ordering. + //!comp(x, y) != comp(x, y) + + auto & mx = cat.items[x]; + auto & my = cat.items[y]; + + if (mx.cells->size() <= sorted_index_ || my.cells->size() <= sorted_index_) + { + std::string a; + if (mx.cells->size() > sorted_index_) + a = (*mx.cells)[sorted_index_].text; + + std::string b; + if (my.cells->size() > sorted_index_) + b = (*my.cells)[sorted_index_].text; + + return weak_ordering_comp(a, mx.anyobj.get(), b, my.anyobj.get(), sorted_reverse_); + } + + return weak_ordering_comp((*mx.cells)[sorted_index_].text, mx.anyobj.get(), (*my.cells)[sorted_index_].text, my.anyobj.get(), sorted_reverse_); + }); + } } } else { //No user-defined comparer is provided, and default comparer is applying. - for(auto & cat: list_) + for (auto & cat : categories_) { - std::sort(std::begin(cat.sorted), std::end(cat.sorted), [&cat, this](std::size_t x, std::size_t y){ - auto & item_x = cat.items[x]; - auto & item_y = cat.items[y]; + if (cat.model_ptr) + { + std::sort(std::begin(cat.sorted), std::end(cat.sorted), [&cat, this](std::size_t x, std::size_t y){ + auto mx_cells = cat.model_ptr->container()->to_cells(x); + auto my_cells = cat.model_ptr->container()->to_cells(y); - if (item_x.cells.size() <= sorted_index_ || item_y.cells.size() <= sorted_index_) + if (mx_cells.size() <= sorted_index_ || my_cells.size() <= sorted_index_) { std::string a; - if (item_x.cells.size() > sorted_index_) - a = item_x.cells[sorted_index_].text; + if (mx_cells.size() > sorted_index_) + a = mx_cells[sorted_index_].text; std::string b; - if (item_y.cells.size() > sorted_index_) - b = item_y.cells[sorted_index_].text; + if (my_cells.size() > sorted_index_) + b = my_cells[sorted_index_].text; return (sorted_reverse_ ? a > b : a < b); } - auto & a = item_x.cells[sorted_index_].text; - auto & b = item_y.cells[sorted_index_].text; + auto & a = mx_cells[sorted_index_].text; + auto & b = my_cells[sorted_index_].text; return (sorted_reverse_ ? a > b : a < b); }); + } + else + { + std::sort(std::begin(cat.sorted), std::end(cat.sorted), [&cat, this](std::size_t x, std::size_t y){ + + auto & mx = cat.items[x]; + auto & my = cat.items[y]; + + if (mx.cells->size() <= sorted_index_ || my.cells->size() <= sorted_index_) + { + std::string a; + if (mx.cells->size() > sorted_index_) + a = (*mx.cells)[sorted_index_].text; + + std::string b; + if (my.cells->size() > sorted_index_) + b = (*my.cells)[sorted_index_].text; + + return (sorted_reverse_ ? a > b : a < b); + } + + auto & a = (*mx.cells)[sorted_index_].text; + auto & b = (*my.cells)[sorted_index_].text; + return (sorted_reverse_ ? a > b : a < b); + }); + } } } scroll_refresh(); } + void scroll_refresh(); /// sort() and ivalidate any existing reference from display position to absolute item, that is after sort() display offset point to different items @@ -855,53 +918,89 @@ namespace nana void scroll(const index_pair& pos, bool to_bottom); - ///Append a new category with a specified name and return a pointer to it. + /// Append a new category with a specified name and return a pointer to it. category_t* create_cat(native_string_type&& text) { - list_.emplace_back(std::move(text)); - return &list_.back(); + categories_.emplace_back(std::move(text)); + return &categories_.back(); } + /// will use the key to insert new cat before the first cat with compare less than the key, or at the end of the list of cat and return a ref to that new cat. ? - category_t* create_cat(std::shared_ptr ptr) + category_t* create_cat(std::shared_ptr& ptr) { - for (auto i = list_.begin(); i != list_.end(); ++i) + for (auto i = categories_.begin(); i != categories_.end(); ++i) { - if (i->key_ptr && i->key_ptr->compare(ptr.get())) + if (i->key_ptr) { - i = list_.emplace(i); - i->key_ptr = ptr; - return &(*i); + if (!i->key_ptr->same_type(ptr.get())) + { + this->ordered_categories_ = false; + break; + } + else if (ptr->compare(i->key_ptr.get())) + { + i = categories_.emplace(i); + i->key_ptr = ptr; + return &(*i); + } } } - list_.emplace_back(); - list_.back().key_ptr = ptr; - return &list_.back(); + categories_.emplace_back(); + categories_.back().key_ptr = ptr; + return &categories_.back(); } - /// add a new cat created at "pos" and return a ref to it + + /// add a new cat created at "pos" and return a ref to it category_t* create_cat(std::size_t pos, native_string_type&& text) { - return &(*list_.emplace(this->get(pos), std::move(text))); + return &(*categories_.emplace(this->get(pos), std::move(text))); } /// Insert before item in absolute "pos" a new item with "text" in column 0, and place it in last display position of this cat - bool insert(const index_pair& pos, std::string&& text) + void insert(const index_pair& pos, std::string&& text, std::size_t columns) { auto & catobj = *get(pos.cat); const auto n = catobj.items.size(); if (pos.item > n) - return false; + throw std::out_of_range("listbox: insert an item at invalid position"); catobj.sorted.push_back(n); - if (pos.item < n) - catobj.items.emplace(catobj.items.begin() + pos.item, std::move(text)); - else - catobj.items.emplace_back(std::move(text)); - return true; + if (catobj.model_ptr) + { + auto container = catobj.model_ptr->container(); + std::size_t item_index; + // + if (pos.item < n) + { + catobj.items.emplace(catobj.items.begin() + pos.item); + container->emplace(pos.item); + item_index = pos.item; + } + else + { + item_index = container->size(); + catobj.items.emplace_back(); + container->emplace_back(); + } + + std::vector cells{ columns }; + cells[0] = std::move(text); + container->assign(item_index, cells); + } + else + { + if (pos.item < n) + catobj.items.emplace(catobj.items.begin() + pos.item, std::move(text)); + else + catobj.items.emplace_back(std::move(text)); + + catobj.items.back().cells->emplace_back(std::move(text)); + } } /// convert from display order to absolute (find the real item in that display pos) but without check from current active sorting, in fact using just the last sorting !!! @@ -928,11 +1027,62 @@ namespace nana return npos ; } + static void throw_if_immutable_model(model_interface* model) + { + if (model && model->container()->immutable()) + { + //Precondition check for the insert/erase operation, it throws if the model is immutable + throw std::runtime_error("nana::listbox disallow to insert/remove items because of immutable model"); + } + } + + void throw_if_immutable_model(const index_pair& pos) const + { + if (pos.cat < categories_.size()) + { + auto i = categories_.cbegin(); + std::advance(i, pos.cat); + + throw_if_immutable_model(i->model_ptr.get()); + } + } + + void assign_model(const index_pair& pos, const std::vector& cells) + { + if (pos.cat < categories_.size()) + { + auto i = categories_.cbegin(); + std::advance(i, pos.cat); + if (i->model_ptr) + { + throw_if_immutable_model(i->model_ptr.get()); + + i->model_ptr->container()->assign(pos.item, cells); + } + } + } + + bool have_model(const index_pair& pos) const + { + return (get(pos.cat)->model_ptr != nullptr); + } + category_t::container::value_type& at_abs(const index_pair& pos) { return get(pos.cat)->items.at(pos.item); } + std::vector at_model_abs(const index_pair& pos) const + { + auto model_ptr = get(pos.cat)->model_ptr.get(); + + model_lock_guard lock(model_ptr); + if (model_ptr) + return model_ptr->container()->to_cells(pos.item); + + return{}; + } + /// return a ref to the real item object at display!!! position pos using current sorting only if it is active, and at absolute position if no sorting is currently active. category_t::container::value_type& at(const index_pair& pos) { @@ -944,6 +1094,22 @@ namespace nana return get(pos.cat)->items.at(index); } + std::vector at_model(const index_pair& pos) const + { + auto model_ptr = get(pos.cat)->model_ptr.get(); + if (!model_ptr) + return{}; + + model_lock_guard lock(model_ptr); + + auto index = pos.item; + + if (sorted_index_ != npos) + index = absolute(pos); + + return model_ptr->container()->to_cells(index); + } + const category_t::container::value_type& at(const index_pair& pos) const { auto index = pos.item; @@ -954,19 +1120,36 @@ namespace nana return get(pos.cat)->items.at(index); } + // Removes all items of a specified category + // It throws when the category is out of range or has an immutable model. void clear(size_type cat) { auto& catobj = *get(cat); + + model_lock_guard lock(catobj.model_ptr.get()); + if (catobj.model_ptr) + { + //The immutable modal can't be cleared. + throw_if_immutable_model(catobj.model_ptr.get()); + + catobj.model_ptr->container()->clear(); + } + catobj.items.clear(); catobj.sorted.clear(); } - /// clear all items in all cat, but not the container of cat self. + + // Clears all items in all cat, but not the container of cat self. void clear() { - for(auto & m : list_) + // Check whether there is a immutable model + for (auto & cat : categories_) + throw_if_immutable_model(cat.model_ptr.get()); + + auto n = categories_.size(); + for (decltype(n) i = 0; i < n; ++i) { - m.items.clear(); - m.sorted.clear(); + clear(i); } } @@ -1048,7 +1231,7 @@ namespace nana else n = i->items.size() - (from.item + 1); - for(++i, ++from.cat; i != list_.end(); ++i, ++from.cat) + for(++i, ++from.cat; i != categories_.end(); ++i, ++from.cat) { ++n; //this is a category if(from.cat != to.cat) @@ -1066,31 +1249,58 @@ namespace nana return n; } - std::vector& get_cells(category_t * cat, size_type pos) const + std::vector& get_cells(category_t * cat, std::size_t pos) const { if (!cat) throw std::out_of_range("nana::listbox: category is null"); - return cat->items.at(pos).cells; + if (cat->model_ptr) + throw std::runtime_error("nana::listbox disallow to get item cells, because there are model cells"); + + return *(cat->items.at(pos).cells); + } + + std::vector get_model_cells(category_t* cat, std::size_t pos) const + { + if (!cat) + throw std::out_of_range("nana::listbox: category is null"); + + if (!(cat->model_ptr)) + throw std::runtime_error("nana::listbox: the category hasn't a model"); + + return cat->model_ptr->container()->to_cells(pos); } void text(category_t* cat, size_type pos, size_type col, cell&& cl, size_type columns) { if ((col < columns) && (pos < cat->items.size())) { - auto & cont = cat->items[pos].cells; - if (col < cont.size()) + std::vector model_cells; + + model_lock_guard lock(cat->model_ptr.get()); + if (cat->model_ptr) { - cont[col] = std::move(cl); + throw_if_immutable_model(cat->model_ptr.get()); + model_cells = cat->model_ptr->container()->to_cells(pos); + } + + auto & cells = (cat->model_ptr ? model_cells : *(cat->items[pos].cells)); + + if (col < cells.size()) + { + cells[col] = std::move(cl); if (sorted_index_ == col) sort(); } else { //If the index of specified sub item is over the number of sub items that item contained, //it fills the non-exist items. - cont.resize(col); - cont.emplace_back(std::move(cl)); + cells.resize(col); + cells.emplace_back(std::move(cl)); } + + if (cat->model_ptr) + cat->model_ptr->container()->assign(pos, model_cells); } } @@ -1098,19 +1308,32 @@ namespace nana { if ((col < columns) && (pos < cat->items.size())) { - auto & cont = cat->items[pos].cells; - if (col < cont.size()) + std::vector model_cells; + + model_lock_guard lock(cat->model_ptr.get()); + if (cat->model_ptr) { - cont[col].text = std::move(str); + throw_if_immutable_model(cat->model_ptr.get()); + model_cells = cat->model_ptr->container()->to_cells(pos); + } + + auto & cells = (cat->model_ptr ? model_cells : *(cat->items[pos].cells)); + + if (col < cells.size()) + { + cells[col].text.swap(str); if (sorted_index_ == col) sort(); } else { //If the index of specified sub item is over the number of sub items that item contained, //it fills the non-exist items. - cont.resize(col); - cont.emplace_back(std::move(str)); + cells.resize(col); + cells.emplace_back(std::move(str)); } + + if (cat->model_ptr) + cat->model_ptr->container()->assign(pos, model_cells); } } @@ -1123,21 +1346,35 @@ namespace nana //If the category is the first one, it just clears the items instead of removing whole category. if(0 == cat) { + if (i->model_ptr) + { + throw_if_immutable_model(i->model_ptr.get()); + i->model_ptr->container()->clear(); + } + i->items.clear(); i->sorted.clear(); } else - list_.erase(i); + categories_.erase(i); } void erase() { //Do not remove the first category. - auto i = list_.begin(); + auto i = categories_.begin(); + + if (i->model_ptr) + { + throw_if_immutable_model(i->model_ptr.get()); + i->model_ptr->container()->clear(); + } + i->items.clear(); i->sorted.clear(); - if(list_.size() > 1) - list_.erase(++i, list_.end()); + + if (categories_.size() > 1) + categories_.erase(++i, categories_.end()); } bool expand(size_type cat, bool exp) @@ -1161,21 +1398,40 @@ namespace nana container& cat_container() { - return list_; + return categories_; } const container& cat_container() const { - return list_; + return categories_; } //Enable/Disable the ordered categories bool enable_ordered(bool enb) { - if (ordered_categories_ == enb) - return false; + if (ordered_categories_ != enb) + { + if (enb) + { + ::nana::detail::key_interface * refkey = nullptr; - ordered_categories_ = enb; + for (auto & cat : categories_) + { + if (!cat.key_ptr) + continue; + + if (refkey) + { + if (!cat.key_ptr->same_type(refkey)) + return false; + } + else + refkey = cat.key_ptr.get(); + } + } + + ordered_categories_ = enb; + } return true; } @@ -1186,8 +1442,8 @@ namespace nana size_type the_number_of_expanded() const { - size_type n = list_.size() - 1; - for(auto & i : list_) + size_type n = categories_.size() - 1; + for (auto & i : categories_) { if(i.expand) n += i.items.size(); @@ -1198,7 +1454,7 @@ namespace nana void check_for_all(bool ck) { index_pair pos; - for(auto & cat : list_) + for (auto & cat : categories_) { pos.item = 0; for(auto & m : cat.items) @@ -1207,8 +1463,8 @@ namespace nana { m.flags.checked = ck; - arg_listbox arg{ item_proxy{ess_, pos}, ck}; - wd_ptr()->events().checked.emit(arg); + arg_listbox arg{ item_proxy{ess_, pos}}; + wd_ptr()->events().checked.emit(arg, wd_ptr()->handle()); } ++pos.item; } @@ -1216,24 +1472,6 @@ namespace nana } } - selection item_checked() const - { - selection vec; - index_pair id; - for(auto & cat : list_) - { - id.item = 0; - for(auto & m : cat.items) - { - if(m.flags.checked) - vec.push_back(id); - ++id.item; - } - ++id.cat; - } - return vec; - } - void select_range(index_pair fr, index_pair to, bool sel) { if (fr > to) @@ -1248,6 +1486,7 @@ namespace nana if (to.is_item()) item_proxy(ess_, to).select(sel); } + void select_display_range(index_pair fr_abs, index_pair to_dpl, bool sel) { index_pair fr_dpl (fr_abs.cat, this->display_order(fr_abs.cat, fr_abs.item)); @@ -1268,7 +1507,7 @@ namespace nana { bool changed = false; index_pair i; - for(auto & cat : list_) + for (auto & cat : categories_) { i.item = 0; for(auto & m : cat.items) @@ -1278,8 +1517,8 @@ namespace nana changed = true; m.flags.selected = sel; - arg_listbox arg{ item_proxy(ess_, i), sel }; - wd_ptr()->events().selected.emit(arg); + arg_listbox arg{ item_proxy(ess_, i) }; + wd_ptr()->events().selected.emit(arg, wd_ptr()->handle()); if (m.flags.selected) last_selected_abs = i; @@ -1293,27 +1532,31 @@ namespace nana return changed; } + /// return absolute positions, no relative to display - void item_selected(selection& vec) const // change to selection item_selected(); + index_pairs pick_items(bool for_selection) const { + index_pairs results; index_pair id; - for(auto & cat : list_) + + for (auto & cat : categories_) { id.item = 0; - for(auto & m : cat.items) + for (auto & m : cat.items) { - if(m.flags.selected) - vec.push_back(id); // absolute positions, no relative to display + if (for_selection ? m.flags.selected : m.flags.checked) + results.push_back(id); // absolute positions, no relative to display ++id.item; } ++id.cat; } + return results; } index_pair find_first_selected() { index_pair id; - for(auto & cat : list_) + for (auto & cat : categories_) { id.item = 0; for(auto & m : cat.items) @@ -1324,16 +1567,16 @@ namespace nana } ++id.cat; } - return {npos,npos}; + return index_pair{npos,npos}; } /// return absolute positions, no relative to display - bool item_selected_all_checked(selection& vec) const + bool item_selected_all_checked(index_pairs& vec) const { index_pair id; bool ck = true; - for (auto & cat : list_) + for (auto & cat : categories_) { id.item = 0; for (auto & m : cat.items) @@ -1367,16 +1610,16 @@ namespace nana auto do_cancel = [this, for_selection](category_t::container::value_type& m, std::size_t cat_pos, std::size_t item_pos) { - arg_listbox arg{ item_proxy(ess_, index_pair(cat_pos, item_pos)), false }; + arg_listbox arg{ item_proxy(ess_, index_pair(cat_pos, item_pos)) }; if (for_selection) { m.flags.selected = false; - widget_->events().selected.emit(arg); + widget_->events().selected.emit(arg, widget_->handle()); } else { m.flags.checked = false; - widget_->events().checked.emit(arg); + widget_->events().checked.emit(arg, widget_->handle()); } }; @@ -1396,7 +1639,7 @@ namespace nana else { std::size_t cat_pos = 0; - for (auto & cat : list_) + for (auto & cat : categories_) { if (cat_pos != except.cat) { @@ -1444,63 +1687,67 @@ namespace nana single = true; limited = category_limited; - auto pred = [for_selection](category_t::container::value_type & m){ - return (for_selection ? m.flags.selected : m.flags.checked); - }; + auto pred = [for_selection](category_t::container::value_type & m){ + return (for_selection ? m.flags.selected : m.flags.checked); + }; - auto cancel = [this, for_selection](category_t::container::value_type& m, std::size_t cat_pos, std::size_t item_pos) + auto cancel = [this, for_selection](category_t::container::value_type& m, std::size_t cat_pos, std::size_t item_pos) + { + arg_listbox arg{ item_proxy(ess_, index_pair(cat_pos, item_pos)) }; + if (for_selection) { - arg_listbox arg{ item_proxy(ess_, index_pair(cat_pos, item_pos)), false }; - if (for_selection) - { - m.flags.selected = false; - widget_->events().selected.emit(arg); - } - else - { - m.flags.checked = false; - widget_->events().checked.emit(arg); - } - }; + m.flags.selected = false; + widget_->events().selected.emit(arg, widget_->handle()); + } + else + { + m.flags.checked = false; + widget_->events().checked.emit(arg, widget_->handle()); + } + }; - std::size_t cat_pos = 0; - if (category_limited) + std::size_t cat_pos = 0; + if (category_limited) + { + for (auto & cat : categories_) { - for (auto & cat : list_) + auto i = std::find_if(cat.items.begin(), cat.items.end(), pred); + if (i != cat.items.end()) { - auto i = std::find_if(cat.items.begin(), cat.items.end(), pred); - if (i != cat.items.end()) + ++i; + for (auto end = cat.items.end(); i != end; ++i) { - ++i; - for (auto end = cat.items.end(); i != end; ++i) + if (pred(*i)) + cancel(*i, cat_pos, i - cat.items.begin()); + } + } + ++cat_pos; + } + } + else + { + bool selected = false; + for (auto & cat : categories_) + { + if(!selected) + { + const auto end = cat.items.end(); + + auto i = std::find_if(cat.items.begin(), end, pred); + if (i != end) + { + selected = true; + + for (++i; i != end; ++i) { if (pred(*i)) cancel(*i, cat_pos, i - cat.items.begin()); } } - ++cat_pos; } - } - else - { - bool selected = false; - for (auto & cat : list_) + else { - if (!selected) - { - auto i = std::find_if(cat.items.begin(), cat.items.end(), pred); - if (i != cat.items.end()) - { - selected = true; - ++i; - for (auto end = cat.items.end(); i != end; ++i) - { - if (pred(*i)) - cancel(*i, cat_pos, i - cat.items.begin()); - } - } - } - else + for (auto & cat : categories_) { std::size_t item_pos = 0; for (auto & m : cat.items) @@ -1514,6 +1761,7 @@ namespace nana ++cat_pos; } } + } } void disable_single(bool for_selection) @@ -1523,7 +1771,7 @@ namespace nana size_type size_categ() const { - return list_.size(); + return categories_.size(); } size_type size_item(size_type cat) const @@ -1553,8 +1801,8 @@ namespace nana { m.flags.checked = ck; - arg_listbox arg{ item_proxy(ess_, index_pair(cat, index)), ck}; - wd_ptr()->events().checked.emit(arg); + arg_listbox arg{ item_proxy(ess_, index_pair(cat, index)) }; + wd_ptr()->events().checked.emit(arg, widget_->handle()); changed = true; } @@ -1565,7 +1813,7 @@ namespace nana bool categ_checked_reverse(size_type cat_index) { - if(list_.size() > cat_index) + if (categories_.size() > cat_index) return categ_checked(cat_index, !categ_checked(cat_index)); return false; } @@ -1590,11 +1838,11 @@ namespace nana /// can be used as the absolute position of the last absolute item, or as the display pos of the last displayed item index_pair last() const { - index_pair i{ list_.size() - 1, list_.back().items.size() }; + index_pair i{ categories_.size() - 1, categories_.back().items.size() }; if (i.cat) { - if (i.item && list_.back().expand) + if (i.item && categories_.back().expand) --i.item; else i.item = npos; @@ -1608,7 +1856,7 @@ namespace nana /// absolute position of the last displayed item index_pair last_displ() const { - return absolute ( last() ); + return index_pair{ absolute(last()) }; } /// can be used as the absolute position of the first absolute item, or as the display pos of the first displayed item @@ -1621,18 +1869,19 @@ namespace nana /// absolute position of the first displayed item index_pair first_displ() const { - return absolute ( first() ); + return index_pair{ absolute(first()) }; } - + bool good(size_type cat) const { - return (cat < list_.size()); + return (cat < categories_.size()); } bool good(const index_pair& pos) const { - return ((pos.cat < list_.size()) && (pos.item < size_item(pos.cat))); + return ((pos.cat < categories_.size()) && (pos.item < size_item(pos.cat))); } + /// if good return the same item (in arg item), or just the next cat and true, but If fail return false bool good_item(index_pair pos, index_pair& item) const { @@ -1655,7 +1904,7 @@ namespace nana return true; } - if (++i == list_.end()) // item out of range and no more cat + if (++i == categories_.end()) // item out of range and no more cat return false; item.cat = pos.cat + 1; // select the next cat @@ -1675,11 +1924,12 @@ namespace nana return (display_pos.item < catobj.sorted.size() ? catobj.sorted[display_pos.item] : npos); } + index_pair absolute_pair(const index_pair& display_pos) const { //Returns an empty pos if item pos npos auto item_pos = absolute(display_pos); - return {item_pos != npos ? display_pos.cat : npos, item_pos}; + return index_pair{item_pos != npos ? display_pos.cat : npos, item_pos}; } ///Translate absolute position (original data order) into relative position (position in display) @@ -1699,11 +1949,12 @@ namespace nana return npos; } + index_pair relative_pair(const index_pair& pos) const { //Returns an empty pos if item is npos auto item_pos = relative(pos); - return {(item_pos != npos ? pos.cat : npos), item_pos}; + return index_pair{(item_pos != npos ? pos.cat : npos), item_pos}; } /// all arg are relative to display order, or all are absolute, but not mixed @@ -1713,7 +1964,7 @@ namespace nana return false; auto cat = get(from.cat); - auto cat_end = list_.end(); + auto cat_end = categories_.end(); auto items_left = (cat->expand ? cat->items.size() : 0); @@ -1756,11 +2007,8 @@ namespace nana { auto i = get(from.cat); size_type n = (from.is_category() ? 1 : from.item + 2); // ?? - if(n <= offs) - { - offs -= n; - } - else + + if (n > offs) { n -=offs; item.cat = from.cat; @@ -1768,7 +2016,8 @@ namespace nana return true; } - while(i != list_.cbegin()) + offs -= n; + while (i != categories_.cbegin()) { --i; --from.cat; @@ -1792,34 +2041,34 @@ namespace nana /// categories iterator container::iterator get(size_type pos) { - if (pos >= list_.size()) + if (pos >= categories_.size()) throw std::out_of_range("nana::listbox: invalid category index"); - auto i = list_.begin(); + auto i = categories_.begin(); std::advance(i, pos); return i; } container::const_iterator get(size_type pos) const { - if (pos >= list_.size()) + if (pos >= categories_.size()) throw std::out_of_range("nana::listbox: invalid category index"); - auto i = list_.cbegin(); + auto i = categories_.cbegin(); std::advance(i, pos); return i; } public: index_pair last_selected_abs, last_selected_dpl; private: - essence_t * ess_{nullptr}; + essence * ess_{nullptr}; nana::listbox * widget_{nullptr}; std::size_t sorted_index_{npos}; ///< The index of the column used to sort bool resort_{true}; bool sorted_reverse_{false}; bool ordered_categories_{false}; ///< A switch indicates whether the categories are ordered. /// The ordered categories always creates a new category at a proper position(before the first one which is larger than it). - container list_; // rename to categories_ + container categories_; bool single_selection_{ false }; bool single_selection_category_limited_{ false }; @@ -1828,23 +2077,18 @@ namespace nana };//end class es_lister - //struct essence_t - //@brief: this struct gives many data for listbox, - // the state of the struct does not effect on member funcions, therefore all data members are public. - struct essence_t + /// 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 { enum class item_state{normal, highlighted, pressed, grabbed, floated}; enum class parts{unknown = -1, header, lister, checker}; + ::nana::listbox* listbox_ptr{nullptr}; ::nana::listbox::scheme_type* scheme_ptr{nullptr}; ::nana::paint::graphics *graph{nullptr}; bool auto_draw{true}; bool checkable{false}; bool if_image{false}; - unsigned header_size{25}; - unsigned item_size{24}; - unsigned text_height{0}; - unsigned suspension_width{0}; ::nana::listbox::export_options def_exp_options; @@ -1859,8 +2103,13 @@ namespace nana struct scroll_part { - static const unsigned scale = 16; - int offset_x; + static const unsigned scale = 16; // ? + + unsigned x_offset() const + { + return static_cast(h.empty() ? 0 : h.value()); + } + index_pair offset_y_abs, offset_y_dpl; //cat stands for category, item stands for item. "item == npos" means that is a category. // need to be abs??? to see the same item after sort() ?? nana::scroll v; @@ -1881,11 +2130,22 @@ namespace nana std::map>> inline_table, inline_buffered_table; - essence_t() + essence() { - scroll.offset_x = 0; pointer_where.first = parts::unknown; - lister.fetch_ordering_comparer = std::bind(&es_header::fetch_comp, &header, std::placeholders::_1); + + lister.fetch_ordering_comparer = [this](std::size_t pos) -> std::function + { + try + { + return header.at(pos).weak_ordering; + } + catch (...) + { + } + + return {}; + }; } std::string to_string(const export_options& exp_opt) const @@ -1928,8 +2188,17 @@ namespace nana } /// directly set a tested relative display pos - void set_scroll_y_dpl(const index_pair& pos_dpl) + void set_scroll_y_dpl(index_pair pos_dpl) { + if (lister.first() != pos_dpl) + { + //check the pos_dpl to make sure the last item is at bottom of listbox + const auto numbers = this->number_of_lister_items(false); + const auto distance = lister.distance(pos_dpl, lister.last()); + if (numbers > 1 && distance < numbers) + lister.backward(lister.last(), numbers - 1, pos_dpl); + } + scroll.offset_y_dpl = pos_dpl; if (pos_dpl.is_category()) scroll.offset_y_abs = pos_dpl; @@ -1942,12 +2211,12 @@ namespace nana //number_of_lister_item - //@brief: Returns the number of items that are contained in pixels - //@param,with_rest: Means whether including extra one item that is not completely contained in reset pixels. + /// @brief Returns the number of items that are contained on screen. + /// @param with_rest: Means whether including extra one item that is not completely contained in reset pixels. size_type number_of_lister_items(bool with_rest) const { unsigned lister_s = graph->height() - 2 - header_visible_px() - (scroll.h.empty() ? 0 : scroll.scale); - return (lister_s / item_size) + (with_rest && (lister_s % item_size) ? 1 : 0); + return (lister_s / scheme_ptr->item_height) + (with_rest && (lister_s % scheme_ptr->item_height) ? 1 : 0); } //keep the first selected item in the display area: the distances are in display positions! @@ -1981,18 +2250,14 @@ namespace nana adjust_scroll_life(); // call adjust_scroll_value(); //adjust_scroll_value(); // again? } - void trace_item_abs( index_pair abs_pos ) - { - if(abs_pos.item == npos && abs_pos.cat == scroll.offset_y_abs.cat - && scroll.offset_y_abs.item == npos ) // if item==off y and is a cat - return; - - trace_item_dpl( lister.relative_pair(abs_pos)) ; // ??? scroll_y_dpl_refresh() ; - } - void trace_last_selected_item( ) { - trace_item_abs(lister.last_selected_abs); + if (lister.last_selected_abs.item == npos && + lister.last_selected_abs.cat == scroll.offset_y_abs.cat && + scroll.offset_y_abs.item == npos) // if item==off y and is a cat + return; + + trace_item_dpl(lister.relative_pair(lister.last_selected_abs)); // ??? scroll_y_dpl_refresh() ; } void update() @@ -2014,16 +2279,15 @@ namespace nana return; const auto header_px = header.pixels(); - const unsigned window_px = graph_size.width - (4 + (scroll.v.empty() ? 0 : scroll.scale - 1)); + const unsigned window_px = graph_size.width - ext_px; - if (header_px < window_px + scroll.offset_x) - { - scroll.offset_x = header_px - window_px; - } + auto offset_x = scroll.x_offset(); + if (header_px < window_px + offset_x) + offset_x = header_px - window_px; scroll.h.amount(header_px); scroll.h.range(window_px); - scroll.h.value(scroll.offset_x); + scroll.h.value(offset_x); scroll.h.step(graph->text_extent_size(L"W").width); } @@ -2036,12 +2300,12 @@ namespace nana const auto items = lister.the_number_of_expanded(); const auto disp_items = number_of_lister_items(false); - size_type off = lister.distance({ 0, 0 }, scroll.offset_y_dpl); + size_type off = lister.distance(index_pair{ 0, 0 }, scroll.offset_y_dpl); if (items < disp_items + off) { index_pair pos; - if (lister.forward({ 0, 0 }, items - disp_items, pos)) + if (lister.forward(index_pair{ 0, 0 }, items - disp_items, pos)) { off = items - disp_items; set_scroll_y_dpl(pos); @@ -2058,24 +2322,43 @@ namespace nana { internal_scope_guard lock; + const unsigned border_px = 1; + const unsigned border_px_twice = (border_px << 1); + const nana::size sz = graph->size(); - unsigned header_s = header.pixels(); - window wd = lister.wd_ptr()->handle(); + + if ((sz.width <= border_px_twice) || (sz.height <= border_px_twice)) + { + scroll.h.close(); + scroll.v.close(); + return; + } + + // Adjust the ranged column assume the vertical scrollbar is enabled. + auto range_adjusted = this->header.calc_ranged_columns(sz.width - border_px_twice - scroll.scale); + auto columns_pixels = header.pixels(); //H scroll enabled - bool h = (header_s > sz.width - 4); + //If range_adjusted is true, it indicates no horzontal scroll bar is enabled. + bool enable_horz = ((!range_adjusted) && (columns_pixels + 4 > sz.width)); // 4px = left and right borders(2px) + left and right gaps(2px) - unsigned lister_s = sz.height - 2 - header_visible_px() - (h ? scroll.scale : 0); - size_type screen_number = (lister_s / item_size); + unsigned head_scroll = 2 + header_visible_px() + (enable_horz ? scroll.scale : 0); // 2px left and right gaps(2px) + unsigned lister_s = sz.height > head_scroll ? sz.height - head_scroll : 0; + size_type screen_number = (lister_s / scheme_ptr->item_height); //V scroll enabled - bool v = (lister.the_number_of_expanded() > screen_number); + auto enable_vert = (lister.the_number_of_expanded() > screen_number); - if(v == true && h == false) - h = (header_s > (sz.width - 2 - scroll.scale)); - - unsigned width = sz.width - 2 - (v ? scroll.scale : 0); - unsigned height = sz.height - 2 - (h ? scroll.scale : 0); + if (enable_vert) + { + if (!enable_horz) + enable_horz = ((columns_pixels + 2 + scroll.scale) > sz.width); + } + else if (range_adjusted) + { + //No vertical scrollbar, then re-adjust the range columns for a new width that excludes vert scroll. + this->header.calc_ranged_columns(sz.width - border_px_twice); + } //event hander for scrollbars auto evt_fn = [this](const arg_scroll& arg) @@ -2090,19 +2373,36 @@ namespace nana set_scroll_y_dpl(item); } - else - scroll.offset_x = static_cast(scroll.h.value()); API::refresh_window(this->lister.wd_ptr()->handle()); }; - if(h) + unsigned horz_px = sz.width - border_px_twice; + if (enable_vert) { - rectangle r(1, sz.height - scroll.scale - 1, width, scroll.scale); + if (horz_px < scroll.scale) + horz_px = 0; + else + horz_px -= scroll.scale; + } + + unsigned vert_px = sz.height - border_px_twice; + if (enable_horz) + { + if (vert_px < scroll.scale) + vert_px = 0; + else + vert_px -= scroll.scale; + } + + const auto wd_handle = lister.wd_ptr()->handle(); + if (enable_horz && horz_px) + { + rectangle r(border_px, static_cast(sz.height - border_px) - static_cast(scroll.scale), horz_px, scroll.scale); if(scroll.h.empty()) { - scroll.h.create(wd, r); - API::take_active(scroll.h.handle(), false, wd); + scroll.h.create(wd_handle, r); + API::take_active(scroll.h.handle(), false, wd_handle); scroll.h.events().value_changed.connect_unignorable(evt_fn); } else @@ -2111,13 +2411,13 @@ namespace nana else if(!scroll.h.empty()) scroll.h.close(); - if(v) + if (enable_vert && vert_px) { - rectangle r(sz.width - 1 - scroll.scale, 1, scroll.scale, height); + rectangle r(static_cast(sz.width - border_px) - static_cast(scroll.scale), border_px, scroll.scale, vert_px); if(scroll.v.empty()) { - scroll.v.create(wd, r); - API::take_active(scroll.v.handle(), false, wd); + scroll.v.create(wd_handle, r); + API::take_active(scroll.v.handle(), false, wd_handle); scroll.v.events().value_changed.connect_unignorable(evt_fn); } else @@ -2127,19 +2427,7 @@ namespace nana else if(!scroll.v.empty()) { scroll.v.close(); - set_scroll_y_dpl({0,0}); - - nana::rectangle r; - if(rect_header(r)) - { - if(header_s > r.width) - { - if((header_s - scroll.offset_x) < r.width) - scroll.offset_x = header_s - r.width; - } - else - scroll.offset_x = 0; - } + set_scroll_y_dpl(index_pair{0,0}); } adjust_scroll_value(); } @@ -2157,38 +2445,44 @@ namespace nana } } - nana::rectangle checkarea(int x, int y) const + nana::rectangle checkarea(int x, int y) const /// move to scheme ?? 16 ? { - return nana::rectangle(x + 4, y + (item_size - 16) / 2, 16, 16); + return nana::rectangle(x + 4, y + (static_cast(scheme_ptr->item_height) - 16) / 2, 16, 16); } int item_xpos(const nana::rectangle& r) const { auto seq = header_seq(r.width); - return (seq.size() ? (header.item_pos(seq[0], nullptr) - scroll.offset_x + r.x) : 0); + + if (seq.empty()) + return 0; + + return (header.position(seq[0], nullptr) - static_cast(scroll.x_offset()) + r.x); } - std::pair where(int x, int y){ + std::pair where(int x, int y) + { std::pair new_where; if(2 < x && x < static_cast(graph->width()) - 2 && 1 < y && y < static_cast(graph->height()) - 1) - { - if(header.visible() && y < static_cast(header_size + 1)) - { - x -= (2 - scroll.offset_x); + { /// we are inside + + if(header.visible() && y < static_cast(scheme_ptr->header_height + 1)) + { /// we are in the header + x += static_cast(scroll.x_offset()) - 2; new_where.first = parts::header; - new_where.second = static_cast(header.item_by_x(x)); + new_where.second = header.column_from_point(x); } else { - new_where.second = ((y + 1) - header_visible_px()) / item_size; // y>1 ! + new_where.second = ((y + 1) - header_visible_px()) / scheme_ptr->item_height; // y>1 ! new_where.first = parts::lister; if(checkable) { nana::rectangle r; if(rect_lister(r)) { - std::size_t top = new_where.second * item_size + header_visible_px(); + auto top = new_where.second * scheme_ptr->item_height + header_visible_px(); if(checkarea(item_xpos(r), static_cast(top)).is_hit(x, y)) new_where.first = parts::checker; } @@ -2203,9 +2497,9 @@ namespace nana return new_where; } - bool calc_where(int x, int y) + bool calc_where(const point& pos) { - std::pair< parts, size_t > new_where=where(x,y); + auto new_where = where(pos.x, pos.y); if (new_where == pointer_where) return false; @@ -2216,7 +2510,8 @@ namespace nana void widget_to_header(nana::point& pos) { --pos.y; - pos.x += (scroll.offset_x - 2); + + pos.x += static_cast(scroll.x_offset()) - 2; } bool rect_header(nana::rectangle& r) const @@ -2225,8 +2520,8 @@ namespace nana { if (lister.wd_ptr()->borderless()) { - r = graph->size(); - r.height = header_size; + r.dimension(graph->size()); + r.height = scheme_ptr->header_height; return !r.empty(); } @@ -2236,7 +2531,7 @@ namespace nana r.x = 2; r.y = 1; r.width = graph->width() - ex_width; - r.height = header_size; + r.height = scheme_ptr->header_height; return true; } } @@ -2245,7 +2540,7 @@ namespace nana unsigned header_visible_px() const { - return (header.visible() ? header_size : 0); + return (header.visible() ? scheme_ptr->header_height : 0); } bool rect_lister(nana::rectangle& r) const @@ -2282,10 +2577,13 @@ namespace nana return false; index_pair target; - if(upwards == false) - lister.forward(scroll.offset_y_dpl, 1, target); + if (upwards == false) + { + if (!lister.forward(scroll.offset_y_dpl, this->scheme_ptr->mouse_wheel.lines, target)) + return false; + } else - lister.backward(scroll.offset_y_dpl, 1, target); + lister.backward(scroll.offset_y_dpl, this->scheme_ptr->mouse_wheel.lines, target); if (target == scroll.offset_y_dpl) return false; @@ -2297,41 +2595,23 @@ namespace nana std::vector header_seq(unsigned lister_w)const { std::vector seqs; - int x = -(scroll.offset_x); - for (const auto& hd : header.cont()) + int x = -static_cast(scroll.x_offset()); + + for (const auto& col : header.cont()) { - if (false == hd.visible) continue; - x += static_cast(hd.pixels); + if (!col.visible_state) + continue; + + x += col.width_px; if (x > 0) - seqs.push_back(hd.index); + seqs.push_back(col.index); + if (x >= static_cast(lister_w)) break; } - return seqs; } - unsigned auto_width(size_type pos, unsigned max = 3000) /// \todo introduce parametr max_header_width - { - unsigned max_w{ 0 }; - for (const auto &cat : lister.cat_container()) - for (const auto &it : cat.items) - { - if (pos >= it.cells.size()) continue; - // precalcule text geometry - unsigned ts = static_cast (graph->text_extent_size(it.cells[pos].text).width); - if (max_w < ts) - max_w = ts; - } - if (!max_w) return 0; - - unsigned ext_w = scheme_ptr->ext_w; - if (pos == 0 && checkable) // only before the first column (display_order=0 ?) - ext_w += 18; - header.item_width(pos, max_w + ext_w + 1 < max ? max_w + ext_w + 1 : max); - return max_w; - } - inline_pane * open_inline(pat::abstract_factory* factory, inline_indicator* indicator) { std::unique_ptr pane_ptr; @@ -2362,18 +2642,338 @@ namespace nana } }; + //definition of iresolver/oresolver + oresolver::oresolver(essence* ess) + : ess_(ess) + {} + + oresolver& oresolver::operator<<(bool n) + { + cells_.emplace_back(std::string(n ? "true" : "false")); + return *this; + } + oresolver& oresolver::operator<<(short n) + { + cells_.emplace_back(std::to_string(n)); + return *this; + } + + oresolver& oresolver::operator<<(unsigned short n) + { + cells_.emplace_back(std::to_string(n)); + return *this; + } + + oresolver& oresolver::operator<<(int n) + { + cells_.emplace_back(std::to_string(n)); + return *this; + } + + oresolver& oresolver::operator<<(unsigned int n) + { + cells_.emplace_back(std::to_string(n)); + return *this; + } + + oresolver& oresolver::operator<<(long n) + { + cells_.emplace_back(std::to_string(n)); + return *this; + } + + oresolver& oresolver::operator<<(unsigned long n) + { + cells_.emplace_back(std::to_string(n)); + return *this; + } + oresolver& oresolver::operator<<(long long n) + { + cells_.emplace_back(std::to_string(n)); + return *this; + } + + oresolver& oresolver::operator<<(unsigned long long n) + { + cells_.emplace_back(std::to_string(n)); + return *this; + } + + oresolver& oresolver::operator<<(float f) + { + cells_.emplace_back(std::to_string(f)); + return *this; + } + + oresolver& oresolver::operator<<(double f) + { + cells_.emplace_back(std::to_string(f)); + return *this; + } + + oresolver& oresolver::operator<<(long double f) + { + cells_.emplace_back(std::to_string(f)); + return *this; + } + + oresolver& oresolver::operator<<(const char* text) + { + cells_.emplace_back(std::string(text)); + return *this; + } + + oresolver& oresolver::operator<<(const wchar_t* text) + { + cells_.emplace_back(to_utf8(text)); + return *this; + } + + oresolver& oresolver::operator<<(const std::string& text) + { + cells_.emplace_back(text); + return *this; + } + + oresolver& oresolver::operator<<(const std::wstring& text) + { + cells_.emplace_back(to_utf8(text)); + return *this; + } + + oresolver& oresolver::operator<<(std::wstring&& text) + { + cells_.emplace_back(to_utf8(text)); + return *this; + } + + oresolver& oresolver::operator<<(cell cl) + { + cells_.emplace_back(std::move(cl)); + return *this; + } + + oresolver& oresolver::operator<<(std::nullptr_t) + { + cells_.emplace_back(); + cells_.back().text.assign(1, wchar_t(0)); //means invalid cell + return *this; + } + + std::vector&& oresolver::move_cells() + { + return std::move(cells_); + } + + ::nana::listbox& oresolver::listbox() + { + return *ess_->listbox_ptr; + } + + iresolver& iresolver::operator>>(bool& n) + { + if (pos_ < cells_.size()) + n = (std::stoi(cells_[pos_++].text) == 0); + return *this; + } + + iresolver& iresolver::operator>>(short& n) + { + if (pos_ < cells_.size()) + n = std::stoi(cells_[pos_++].text); + return *this; + } + + iresolver& iresolver::operator>>(unsigned short& n) + { + if (pos_ < cells_.size()) + n = static_cast(std::stoul(cells_[pos_++].text)); + return *this; + } + + iresolver& iresolver::operator>>(int& n) + { + if (pos_ < cells_.size()) + n = std::stoi(cells_[pos_++].text); + return *this; + } + + iresolver& iresolver::operator>>(unsigned int& n) + { + if (pos_ < cells_.size()) + n = std::stoul(cells_[pos_++].text); + return *this; + } + + iresolver& iresolver::operator>>(long& n) + { + if (pos_ < cells_.size()) + n = std::stol(cells_[pos_++].text); + return *this; + } + + iresolver& iresolver::operator>>(unsigned long& n) + { + if (pos_ < cells_.size()) + n = std::stoul(cells_[pos_++].text); + return *this; + } + + iresolver& iresolver::operator>>(long long& n) + { + if (pos_ < cells_.size()) + n = std::stoll(cells_[pos_++].text); + return *this; + } + iresolver& iresolver::operator>>(unsigned long long& n) + { + if (pos_ < cells_.size()) + n = std::stoull(cells_[pos_++].text); + return *this; + } + iresolver& iresolver::operator>>(float& f) + { + if (pos_ < cells_.size()) + f = std::stof(cells_[pos_++].text); + return *this; + } + + + iresolver& iresolver::operator>>(double& f) + { + if (pos_ < cells_.size()) + f = std::stod(cells_[pos_++].text); + return *this; + } + + iresolver& iresolver::operator>>(long double& f) + { + if (pos_ < cells_.size()) + f = std::stold(cells_[pos_++].text); + return *this; + } + + iresolver& iresolver::operator>>(std::string& text) + { + if (pos_ < cells_.size()) + text = cells_[pos_++].text; + return *this; + } + + iresolver& iresolver::operator>>(std::wstring& text) + { + if (pos_ < cells_.size()) + text = to_wstring(cells_[pos_++].text); + + return *this; + } + + iresolver::iresolver(const std::vector& cl) + : cells_(cl) + {} + + iresolver& iresolver::operator>>(cell& cl) + { + if (pos_ < cells_.size()) + cl = cells_[pos_++]; + return *this; + } + + iresolver& iresolver::operator>>(std::nullptr_t) + { + ++pos_; + return *this; + } + //end class iresolver/oresolver + + unsigned es_lister::column_content_pixels(size_type pos) const + { + unsigned max_px = 0; + for (auto & cat : categories_) + { + if (cat.model_ptr) + { + for (std::size_t i = 0; i < cat.items.size(); ++i) + { + auto model_cells = cat.model_ptr->container()->to_cells(i); + if (pos >= model_cells.size()) + continue; + + auto content_px = ess_->graph->text_extent_size(model_cells[pos].text).width; + if (content_px > max_px) + max_px = content_px; + } + } + else + { + for (auto & m : cat.items) + { + if (pos >= m.cells->size()) + continue; + + auto content_px = ess_->graph->text_extent_size((*m.cells)[pos].text).width; + if (content_px > max_px) + max_px = content_px; + } + } + } + return max_px; + } + + //es_header::column member functions + void es_header::column::_m_refresh() + { + ess_->adjust_scroll_life(); + API::refresh_window(ess_->lister.wd_ptr()->handle()); + } + + void es_header::column::fit_content(unsigned maximize) noexcept + { + auto content_px = ess_->lister.column_content_pixels(index); + + if (0 == content_px) + return; + + content_px += (ess_->scheme_ptr->text_margin * 2); //margin at left/right end. + + if (index == 0 && ess_->checkable) // only before the first column (display_order=0 ?) + content_px += 18; // add to geom. scheme (width of the checker) ?? + + if (range_width_px.first != range_width_px.second) + { + if (range_width_px.first > content_px) + content_px = range_width_px.first; + + //Use range_width_px defined max if maximize is unspecified + if (0 == maximize) + maximize = range_width_px.second; + } + + if (0 == maximize) + maximize = ess_->scheme_ptr->max_fit_content; + + //maximize is only available when it > 0 + if (maximize && (content_px > maximize)) + content_px = maximize; + + width_px = content_px; + + _m_refresh(); + } + //end es_header::column functions + class inline_indicator : public ::nana::detail::inline_widget_indicator { public: - using parts = essence_t::parts; + using parts = essence::parts; - inline_indicator(essence_t* ess, std::size_t column_pos) + inline_indicator(essence* ess, std::size_t column_pos) : ess_{ ess }, column_pos_{column_pos} { } - void attach(index_type pos, essence_t::inline_pane* pane) + void attach(index_type pos, essence::inline_pane* pane) { for (auto & pn : panes_) { @@ -2399,7 +2999,11 @@ namespace nana void modify(index_type pos, const value_type& value) const override { - auto & cells = ess_->lister.at_abs(pos).cells; + ess_->lister.throw_if_immutable_model(pos); + + auto model_cells = ess_->lister.at_model_abs(pos); + auto & cells = ess_->lister.have_model(pos) ? model_cells : (*ess_->lister.at_abs(pos).cells); + if (cells.size() <= column_pos_) cells.resize(column_pos_ + 1); @@ -2415,6 +3019,10 @@ namespace nana } cells[column_pos_].text = value; + + if (model_cells.size()) + ess_->lister.assign_model(pos, model_cells); + ess_->update(); } } @@ -2439,16 +3047,16 @@ namespace nana } } private: - essence_t * const ess_; + essence * const ess_; const std::size_t column_pos_; - std::vector> panes_; + std::vector> panes_; }; void es_lister::scroll(const index_pair& pos, bool to_bottom) { auto& cat = *get(pos.cat); - if ((pos.item != ::nana::npos) && (pos.item >= cat.items.size())) + if ((pos.item != nana::npos) && (pos.item >= cat.items.size())) throw std::invalid_argument("listbox: invalid pos to scroll"); if (!cat.expand) @@ -2472,17 +3080,17 @@ namespace nana start_pos = pos; else { - index_pair last(list_.size() - 1); + index_pair last(categories_.size() - 1); - if (list_.back().expand) + if (categories_.back().expand) { - if (list_.back().items.empty()) - last.item = npos; + if (categories_.back().items.empty()) + last.item = nana::npos; else - last.item = list_.back().items.size() - 1; + last.item = categories_.back().items.size() - 1; } else - last.item = ::nana::npos; + last.item = nana::npos; backward(last, view_items, start_pos); } @@ -2494,11 +3102,17 @@ namespace nana void es_lister::erase(const index_pair& pos) { - auto & catobj = *get(pos.cat); - if (pos.item < catobj.items.size()) + auto & cat = *get(pos.cat); + if (pos.item < cat.items.size()) { - catobj.items.erase(catobj.items.begin() + pos.item); - catobj.sorted.erase(std::find(catobj.sorted.begin(), catobj.sorted.end(), catobj.items.size())); + if (cat.model_ptr) + { + throw_if_immutable_model(cat.model_ptr.get()); + cat.model_ptr->container()->erase(pos.item); + } + + cat.items.erase(cat.items.begin() + pos.item); + cat.sorted.erase(std::find(cat.sorted.begin(), cat.sorted.end(), cat.items.size())); sort(); } @@ -2515,7 +3129,7 @@ namespace nana if (next_selected_dpl.empty()) // has no cat ? (cat == npos) => beging from first cat { bool good = false; - for(size_type i = 0, size = list_.size(); i < size; ++i) // run all cat + for (size_type i = 0, size = categories_.size(); i < size; ++i) // run all cat { if(size_item(i)) { @@ -2603,12 +3217,22 @@ namespace nana first=false; else list_str += (to_utf8(cat.text) + exp_opt.endl); + + std::vector model_cells; + + auto const pcell = (cat.model_ptr ? &model_cells : nullptr); for (auto i : cat.sorted) { - auto& it= cat.items[i] ; - if(it.flags.selected || !exp_opt.only_selected_items) - list_str += (it.to_string(exp_opt) + exp_opt.endl); + auto& item = cat.items[i]; + if (item.flags.selected || !exp_opt.only_selected_items) + { + //Test if the category have a model set. + if (pcell) + cat.model_ptr->container()->to_cells(i).swap(model_cells); + + list_str += (item.to_string(exp_opt, pcell) + exp_opt.endl); + } } } return list_str ; @@ -2629,41 +3253,46 @@ namespace nana { public: using graph_reference = nana::paint::graphics&; - using item_state = essence_t::item_state; - using parts = essence_t::parts; + using item_state = essence::item_state; + using parts = essence::parts; - drawer_header_impl(essence_t* es): essence_(es){} + drawer_header_impl(essence* es): essence_(es){} - size_type item_spliter() const + size_type splitter() const { - return item_spliter_; + return grabs_.splitter; } - void cancel_spliter() + void cancel_splitter() { - item_spliter_ = npos; + grabs_.splitter = npos; } - bool mouse_spliter(const nana::rectangle& r, int x) + // 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) { if(essence_->ptr_state == item_state::highlighted) { - x -= (r.x - essence_->scroll.offset_x); - for(auto & hd : essence_->header.cont()) // in current order + x -= r.x - static_cast(essence_->scroll.x_offset()); + + for(auto & col : essence_->header.cont()) // in current order { - if(hd.visible) + if(col.visible_state) { - if((static_cast(hd.pixels) < x + 2) && (x < static_cast(hd.pixels) + 3)) + auto col_pixels = static_cast(col.width_px); + + if ((col_pixels < x + static_cast(essence_->scheme_ptr->header_splitter_area_before)) + && (x < col_pixels + static_cast(essence_->scheme_ptr->header_splitter_area_after))) { - item_spliter_ = hd.index; // original index + grabs_.splitter = col.index; // original index return true; } - x -= hd.pixels; + x -= col_pixels; } } } else if(essence_->ptr_state == item_state::normal) - item_spliter_ = npos; + grabs_.splitter = npos; return false; } @@ -2671,125 +3300,151 @@ namespace nana { if(is_grab) { - ref_xpos_ = pos.x; - if(item_spliter_ != npos) - orig_item_width_ = essence_->header.column(item_spliter_).pixels; + grabs_.start_pos = pos.x; + if(grabs_.splitter != npos) // resize header item, not move it + grabs_.item_width = essence_->header.at(grabs_.splitter).width_px; } else if(grab_terminal_.index != npos && grab_terminal_.index != essence_->pointer_where.second) essence_->header.move(essence_->pointer_where.second, grab_terminal_.index, grab_terminal_.place_front); } //grab_move - //@brief: draw when an item is grabbing. - //@return: 0 = no graphics changed, 1 = just update, 2 = refresh - int grab_move(const nana::rectangle& rect, const nana::point& pos) + /// @brief draw when an item is grabbing. + /// @return true if refresh is needed, false otherwise + bool grab_move(const nana::point& pos) { - if(item_spliter_ == npos) - { - draw(rect); - _m_make_float(rect, pos); - - //Draw the target strip - grab_terminal_.index = _m_target_strip(pos.x, rect, essence_->pointer_where.second, grab_terminal_.place_front); - return 1; + if(npos == grabs_.splitter) + { // move column, not resize it + options_.grab_column = true; + options_.grab_column_position = pos; + return true; } else - { - const auto& item = essence_->header.column(item_spliter_); - //Resize the item specified by item_spliter_. - auto new_w = orig_item_width_ - (ref_xpos_ - pos.x); - if(item.pixels != new_w) - { - essence_->header.item_width(item_spliter_, (new_w < (essence_->suspension_width + 20) ? essence_->suspension_width + 20 : new_w)); - new_w = essence_->header.pixels(); - if(new_w < (rect.width + essence_->scroll.offset_x)) - essence_->scroll.offset_x = (new_w > rect.width ? new_w - rect.width : 0); + { // resize column, not move it + auto& col = essence_->header.at(grabs_.splitter); + auto delta_px = (grabs_.start_pos - pos.x); + + //Resize the item specified by item_spliter_. + auto new_w = static_cast(grabs_.item_width) > delta_px ? grabs_.item_width - delta_px : 0; + + //Check the minimized and maximized value + if (col.range_width_px.first != col.range_width_px.second) + { + //Column ranged width + if (new_w < col.range_width_px.first) + new_w = col.range_width_px.first; + else if (new_w > col.range_width_px.second) + new_w = col.range_width_px.second; + } + else + { + //Default scheme + new_w = (std::max)(new_w, essence_->scheme_ptr->suspension_width + essence_->scheme_ptr->min_column_width); + } + + if(col.width_px != new_w) + { + col.width_px = new_w; essence_->adjust_scroll_life(); - return 2; + return true; } } - return 0; + return false; } - void draw(const nana::rectangle& r) + void draw(graph_reference graph, const nana::rectangle& r) { - graph_reference graph = *(essence_->graph); + const auto border_color = essence_->scheme_ptr->header_bgcolor.get_color().blend(colors::black, 0.8); - int text_top = (r.height - essence_->text_height) / 2 + r.y; + int text_top = (r.height - essence_->scheme_ptr->text_height) / 2 + r.y; auto text_color = essence_->lister.wd_ptr()->fgcolor(); auto state = item_state::normal; //check whether grabing an item, if item_spliter_ != npos, that indicates the grab item is a spliter. - if (essence_->pointer_where.first == parts::header && (item_spliter_ == npos)) + if ((parts::header == essence_->pointer_where.first) && (npos == grabs_.splitter)) state = essence_->ptr_state; - const unsigned height = r.height - 1; - const int bottom_y = r.bottom() - 2; - int x = r.x - essence_->scroll.offset_x; + rectangle column_r{ + r.x - static_cast(essence_->scroll.x_offset()), r.y, + 0, r.height - 1 + }; - for (auto & i : essence_->header.cont()) + for (auto & col : essence_->header.cont()) { - if (i.visible) + if (col.visible_state) { - int next_x = x + static_cast(i.pixels); - if (next_x > r.x) + column_r.width = col.width_px; + + const auto right_pos = column_r.right(); + + //Make sure the column is in the display area. + if (right_pos > r.x) { - _m_draw_header_item(graph, x, r.y, height, text_top, text_color, i, (i.index == essence_->pointer_where.second ? state : item_state::normal)); - graph.line({ next_x - 1, r.y }, { next_x - 1, bottom_y }, _m_border_color()); + _m_draw_header_item(graph, column_r, text_top, text_color, col, (col.index == essence_->pointer_where.second ? state : item_state::normal)); + graph.line({ right_pos - 1, r.y }, { right_pos - 1, r.bottom() - 2 }, /*_m_border_color()*/ border_color); } - x = next_x; - if (x > r.right()) + column_r.x = right_pos; + if (right_pos > r.right()) break; } } - if (x < r.right()) - graph.rectangle({ x, r.y, static_cast(r.right() - x), height }, true, essence_->scheme_ptr->header_bgcolor); + //If the last rendered column's right is less than r.right, fill the spare space. + if (column_r.x < r.right()) + { + column_r.width = (r.right() - column_r.x); + graph.rectangle(column_r, true, essence_->scheme_ptr->header_bgcolor); + } - const int y = r.y + r.height - 1; - essence_->graph->line({ r.x, y }, { r.x + static_cast(r.width), y }, _m_border_color()); + const int y = r.bottom() - 1; + graph.line({ r.x, y }, { r.right(), y }, /*_m_border_color()*/ border_color); + + if (options_.grab_column) + { + _m_make_float(r, options_.grab_column_position); // now draw one floating header item + + //Draw the target strip + grab_terminal_.index = _m_target_strip(options_.grab_column_position.x, r, essence_->pointer_where.second, grab_terminal_.place_front); + + options_.grab_column = false; + } } private: - ::nana::color _m_border_color() const - { - return essence_->scheme_ptr->header_bgcolor.get_color().blend(colors::black, 0.8); - } - size_type _m_target_strip(int x, const nana::rectangle& rect, size_type grab, bool& place_front) { //convert x to header logic coordinate. - if(x < essence_->scroll.offset_x) - x = essence_->scroll.offset_x; - else if(x > essence_->scroll.offset_x + static_cast(rect.width)) - x = essence_->scroll.offset_x + static_cast(rect.width); + const int x_offset = static_cast(essence_->scroll.x_offset()); + if (x < x_offset) + x = x_offset; + else if (x > x_offset + static_cast(rect.width)) + x = x_offset + static_cast(rect.width); - size_type i = essence_->header.item_by_x(x); + auto i = essence_->header.column_from_point(x); if(i == npos) { - i = (essence_->header.item_pos(grab, nullptr) < x ? essence_->header.last() : essence_->header.begin()); + i = (essence_->header.position(grab, nullptr) < x ? essence_->header.last() : essence_->header.begin()); } if(grab != i) { unsigned item_pixels = 0; - auto item_x = essence_->header.item_pos(i, &item_pixels); + auto item_x = essence_->header.position(i, &item_pixels); //Get the item pos //if mouse pos is at left of an item middle, the pos of itself otherwise the pos of the next. place_front = (x <= (item_x + static_cast(item_pixels / 2))); - x = (place_front ? item_x : essence_->header.item_pos(essence_->header.neighbor(i, false), nullptr)); + x = (place_front ? item_x : essence_->header.position(essence_->header.neighbor(i, false), nullptr)); - if(i != npos) - essence_->graph->rectangle({ x - essence_->scroll.offset_x + rect.x, rect.y, 2, rect.height }, true, colors::red); + if (npos != i) + essence_->graph->rectangle({x - x_offset + rect.x, rect.y, 2, rect.height}, true, colors::red); return i; } return npos; } - template - void _m_draw_header_item(graph_reference graph, int x, int y, unsigned height, int txtop, const ::nana::color& fgcolor, const Item& item, item_state state) + void _m_draw_header_item(graph_reference graph, const rectangle& column_r, int text_top, const ::nana::color& fgcolor, const es_header::column& column, item_state state) { ::nana::color bgcolor; switch(state) @@ -2801,50 +3456,81 @@ namespace nana case item_state::floated: bgcolor = essence_->scheme_ptr->header_floated.get_color(); break; } - graph.gradual_rectangle({ x, y, item.pixels, height }, bgcolor.blend(colors::white, 0.9), bgcolor.blend(colors::black, 0.9), true); - graph.string({ x + 5, txtop }, item.text, fgcolor); + graph.gradual_rectangle(column_r, bgcolor.blend(colors::white, 0.9), bgcolor.blend(colors::black, 0.9), true); - if(item.index == essence_->lister.sort_index()) + paint::aligner text_aligner{ graph, column.alignment, column.alignment }; + + auto text_margin = essence_->scheme_ptr->text_margin; + + if (text_margin < column_r.width) + { + graph.palette(true, fgcolor); + + point text_pos{ column_r.x, text_top }; + + if (align::left == column.alignment) + text_pos.x += text_margin; + else if (align::center == column.alignment) + text_margin = 0; + + text_aligner.draw(column.text, text_pos, column_r.width - text_margin); + } + + if (column.index == essence_->lister.sort_index()) { facade arrow("hollow_triangle"); arrow.direction(essence_->lister.sort_reverse() ? ::nana::direction::south : ::nana::direction::north); - arrow.draw(graph, {}, colors::black, { x + static_cast(item.pixels - 16) / 2, -4, 16, 16 }, element_state::normal); + arrow.draw(graph, {}, colors::black, { column_r.x + (static_cast(column_r.width) - 16) / 2, -4, 16, 16 }, element_state::normal); // geometric scheme? } } void _m_make_float(const nana::rectangle& rect, const nana::point& pos) { - const auto & item = essence_->header.column(essence_->pointer_where.second); + const auto & col = essence_->header.at(essence_->pointer_where.second); - nana::paint::graphics ext_graph({ item.pixels, essence_->header_size }); - ext_graph.typeface(essence_->graph->typeface()); + paint::graphics fl_graph({ col.width_px, essence_->scheme_ptr->header_height }); - int txtop = (essence_->header_size - essence_->text_height) / 2; - _m_draw_header_item(ext_graph, 0, 0, essence_->header_size, txtop, colors::white, item, item_state::floated); + fl_graph.typeface(essence_->graph->typeface()); - int xpos = essence_->header.item_pos(item.index, nullptr) + pos.x - ref_xpos_; - ext_graph.blend(rectangle{ ext_graph.size() }, *(essence_->graph), nana::point(xpos - essence_->scroll.offset_x + rect.x, rect.y), 0.5); + int text_top = (essence_->scheme_ptr->header_height - essence_->scheme_ptr->text_height) / 2; + _m_draw_header_item(fl_graph, rectangle{ fl_graph.size()}, text_top, colors::white, col, item_state::floated); + + auto xpos = essence_->header.position(col.index, nullptr) + pos.x - grabs_.start_pos; + + fl_graph.blend(rectangle{ fl_graph.size() }, *(essence_->graph), point{xpos - static_cast(essence_->scroll.x_offset()) + rect.x, rect.y}, 0.5); } private: - int ref_xpos_; - unsigned orig_item_width_; - size_type item_spliter_{npos}; + essence * essence_; + + struct grab_variables + { + int start_pos; + unsigned item_width; + + size_type splitter{ npos }; + }grabs_; + struct grab_terminal { size_type index; bool place_front; }grab_terminal_; - essence_t * essence_; + + struct options + { + bool grab_column{ false }; + point grab_column_position; + }options_; }; class drawer_lister_impl { public: - using item_state = essence_t::item_state; - using parts = essence_t::parts; + using item_state = essence::item_state; + using parts = essence::parts; - drawer_lister_impl(essence_t * es) + drawer_lister_impl(essence * es) :essence_(es) {} @@ -2852,18 +3538,22 @@ namespace nana { internal_scope_guard lock; - size_type n = essence_->number_of_lister_items(true); - if(0 == n)return; + //The count of items to be drawn + auto item_count = essence_->number_of_lister_items(true); + if (0 == item_count) + return; + widget * wdptr = essence_->lister.wd_ptr(); auto bgcolor = wdptr->bgcolor(); auto fgcolor = wdptr->fgcolor(); - unsigned header_w = essence_->header.pixels(); essence_->graph->palette(false, bgcolor); - if(header_w - essence_->scroll.offset_x < rect.width) - essence_->graph->rectangle(rectangle{ point(rect.x + static_cast(header_w)-essence_->scroll.offset_x, rect.y), - size(static_cast(rect.width) + essence_->scroll.offset_x - static_cast(header_w), rect.height) }, - true); + + const auto header_w = essence_->header.pixels(); + const auto x_offset = essence_->scroll.x_offset(); + if (header_w < x_offset + rect.width) + essence_->graph->rectangle(rectangle{ point{ rect.x + static_cast(header_w) - static_cast(x_offset), rect.y }, + size{rect.width + x_offset - header_w, rect.height} }, true); es_lister & lister = essence_->lister; //The Tracker indicates the item where mouse placed. @@ -2881,7 +3571,7 @@ namespace nana int x = essence_->item_xpos(rect); int y = rect.y; - int txtoff = (essence_->item_size - essence_->text_height) / 2; + int txtoff = (essence_->scheme_ptr->item_height - essence_->scheme_ptr->text_height) / 2; auto i_categ = lister.get(essence_->scroll.offset_y_dpl.cat); @@ -2909,48 +3599,64 @@ namespace nana std::size_t size = i_categ->items.size(); index_pair item_index{ idx.cat, 0 }; - for(std::size_t offs = essence_->scroll.offset_y_dpl.item; offs < size; ++offs, ++idx.item) + + for (std::size_t offs = essence_->scroll.offset_y_dpl.item; offs < size; ++offs, ++idx.item) { - if(n-- == 0) break; + if (0 == item_count--) + break; + state = (tracker == idx ? item_state::highlighted : item_state::normal); item_index.item = offs; item_index = lister.absolute_pair(item_index); _m_draw_item(*i_categ, item_index, x, y, txtoff, header_w, rect, subitems, bgcolor,fgcolor, state); - y += essence_->item_size; + y += essence_->scheme_ptr->item_height; } ++i_categ; ++idx.cat; } - for(; i_categ != lister.cat_container().end(); ++i_categ, ++idx.cat) + if (item_count > 0) { - if(n-- == 0) break; - idx.item = 0; - - state = (tracker.is_category() && (idx.cat == tracker.cat) ? item_state::highlighted : item_state::normal); - - _m_draw_categ(*i_categ, rect.x - essence_->scroll.offset_x, y, txtoff, header_w, rect, bgcolor, state); - y += essence_->item_size; - - if(false == i_categ->expand) - continue; - - auto size = i_categ->items.size(); - index_pair item_pos{ idx.cat, 0 }; - for(decltype(size) pos = 0; pos < size; ++pos) + for (; i_categ != lister.cat_container().end(); ++i_categ, ++idx.cat) { - if(n-- == 0) break; - state = (idx == tracker ? item_state::highlighted : item_state::normal); + if (0 == item_count--) + break; - item_pos.item = pos; - item_pos.item = lister.absolute(item_pos); + idx.item = 0; - _m_draw_item(*i_categ, item_pos, x, y, txtoff, header_w, rect, subitems, bgcolor, fgcolor, state); - y += essence_->item_size; - ++idx.item; + state = (tracker.is_category() && (idx.cat == tracker.cat) ? item_state::highlighted : item_state::normal); + + _m_draw_categ(*i_categ, rect.x - static_cast(x_offset), y, txtoff, header_w, rect, bgcolor, state); + y += essence_->scheme_ptr->item_height; + + if (false == i_categ->expand) + continue; + + if (item_count > 0) + { + auto size = i_categ->items.size(); + index_pair item_pos{ idx.cat, 0 }; + for (decltype(size) pos = 0; pos < size; ++pos) + { + if (0 == item_count--) + break; + + state = (idx == tracker ? item_state::highlighted : item_state::normal); + + item_pos.item = pos; + item_pos.item = lister.absolute(item_pos); + + _m_draw_item(*i_categ, item_pos, x, y, txtoff, header_w, rect, subitems, bgcolor, fgcolor, state); + y += essence_->scheme_ptr->item_height; + if (y >= rect.bottom()) + break; + + ++idx.item; + } + } } } @@ -2970,14 +3676,14 @@ namespace nana bgcolor = bgcolor.blend(static_cast(0x99defd), 0.8); auto graph = essence_->graph; - graph->rectangle(rectangle{ x, y, width, essence_->item_size }, true, bgcolor); + graph->rectangle(rectangle{ x, y, width, essence_->scheme_ptr->item_height }, true, bgcolor); color txt_color{ static_cast(0x3399) }; facade arrow("double"); arrow.direction(categ.expand ? ::nana::direction::north : ::nana::direction::south); arrow.draw( *graph, {}, txt_color, - { x + 5, y + static_cast(essence_->item_size - 16) / 2, 16, 16 }, + { x + 5, y + static_cast(essence_->scheme_ptr->item_height - 16) / 2, 16, 16 }, element_state::normal); graph->string({ x + 20, y + txtoff }, categ.text, txt_color); @@ -2989,26 +3695,43 @@ namespace nana graph->string({ x + 25 + static_cast(text_s), y + txtoff }, str); - if (x + 35 + extend_text_w < x + width) + if (x + 35 + static_cast(extend_text_w) < x + static_cast(width)) { - ::nana::point pos{ x + 30 + static_cast(extend_text_w), y + static_cast(essence_->item_size) / 2 }; + ::nana::point pos{ x + 30 + static_cast(extend_text_w), y + static_cast(essence_->scheme_ptr->item_height) / 2 }; graph->line(pos, { x + static_cast(width)-5, pos.y }, txt_color); } //Draw selecting inner rectangle if (sel && (categ.expand == false)) { - width -= essence_->scroll.offset_x; - _m_draw_border(r.x, y, (r.width < width ? r.width : width)); + _m_draw_border(r.x, y, (std::min)(r.width, width - essence_->scroll.x_offset())); } } - //Draws an item - //@param content_r the rectangle of list content - void _m_draw_item(const category_t& cat, const index_pair& item_pos, const int x, const int y, const int txtoff, unsigned width, const nana::rectangle& content_r, const std::vector& seqs, nana::color bgcolor, nana::color fgcolor, item_state state) const + /// Draws an item + void _m_draw_item(const category_t& cat, + const index_pair& item_pos, + const int x, ///< left coordinate ? + const int y, ///< top coordinate + 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 + nana::color bgcolor, + nana::color fgcolor, + item_state state + ) const { auto & item = cat.items[item_pos.item]; + std::vector model_cells; + if (cat.model_ptr) + { + model_cells = cat.model_ptr->container()->to_cells(item_pos.item); + } + + auto & cells = (cat.model_ptr ? model_cells : *item.cells); + if (item.flags.selected) // fetch the "def" colors bgcolor = essence_->scheme_ptr->item_selected; else if (!item.bgcolor.invisible()) @@ -3022,35 +3745,34 @@ namespace nana if (item.flags.selected) bgcolor = bgcolor.blend(colors::black, 0.98); // or "selected" else - bgcolor = bgcolor.blend(essence_->scheme_ptr->item_selected, 0.7); + bgcolor = bgcolor.blend(essence_->scheme_ptr->item_selected, 0.7); /// \todo create a parametre for amount of blend } - unsigned show_w = width - essence_->scroll.offset_x; - if(show_w >= content_r.width) show_w = content_r.width; + unsigned show_w = (std::min)(content_r.width, width - essence_->scroll.x_offset()); auto graph = essence_->graph; - //draw the background - graph->rectangle(rectangle{ content_r.x, y, show_w, essence_->item_size }, true, bgcolor); - int item_xpos = x; - unsigned extreme_text = x; + //draw the background for the whole item + graph->rectangle(rectangle{ content_r.x, y, show_w, essence_->scheme_ptr->item_height }, true, bgcolor); + + int column_x = x; for (size_type display_order{ 0 }; display_order < seqs.size(); ++display_order) // get the cell (column) index in the order headers are displayed { const auto column_pos = seqs[display_order]; - const auto & header = essence_->header.column(column_pos); // deduce the corresponding header which is in a kind of dislay order + const auto & col = essence_->header.at(column_pos); // deduce the corresponding header which is in a kind of dislay order auto it_bgcolor = bgcolor; - if (header.pixels > 5) + if (col.width_px > essence_->scheme_ptr->text_margin) { - int content_pos = 5; + int content_pos = 0; //Draw the image in the 1st column in display order if (0 == display_order) { if (essence_->checkable) { - content_pos += 18; + content_pos += 18; // checker width, geom scheme? element_state estate = element_state::normal; if (essence_->pointer_where.first == parts::checker) @@ -3067,7 +3789,7 @@ namespace nana using state = facade::state; crook_renderer_.check(item.flags.checked ? state::checked : state::unchecked); - crook_renderer_.draw(*graph, bgcolor, fgcolor, essence_->checkarea(item_xpos, y), estate); + crook_renderer_.draw(*graph, bgcolor, fgcolor, essence_->checkarea(column_x, y), estate); } if (essence_->if_image) @@ -3076,28 +3798,28 @@ namespace nana if (item.img) { nana::rectangle img_r(item.img_show_size); - img_r.x = content_pos + item_xpos + static_cast(16 - item.img_show_size.width) / 2; - img_r.y = y + static_cast(essence_->item_size - item.img_show_size.height) / 2; + img_r.x = content_pos + column_x + (16 - static_cast(item.img_show_size.width)) / 2; // center in 16 - geom scheme? + img_r.y = y + (static_cast(essence_->scheme_ptr->item_height) - static_cast(item.img_show_size.height)) / 2; // center item.img.stretch(rectangle{ item.img.size() }, *graph, img_r); } - content_pos += 18; + content_pos += 18; // image width, geom scheme? } } bool draw_column = true; - if (static_cast(content_pos) < header.pixels) + if ( content_pos + essence_->scheme_ptr->text_margin < col.width_px) // we have room { auto inline_wdg = _m_get_inline_pane(cat, column_pos); if (inline_wdg) { //Make sure the user-define inline widgets in right visible rectangle. rectangle pane_r; - auto wdg_x = item_xpos + content_pos; - auto wdg_w = header.pixels - static_cast(content_pos); + auto wdg_x = column_x + content_pos; + auto wdg_w = col.width_px - static_cast(content_pos); bool visible_state = true; - if (::nana::overlap(content_r, { wdg_x, y, wdg_w, essence_->item_size }, pane_r)) + if (::nana::overlap(content_r, { wdg_x, y, wdg_w, essence_->scheme_ptr->item_height }, pane_r)) { ::nana::point pane_pos; if (wdg_x < content_r.x) @@ -3112,7 +3834,7 @@ namespace nana else visible_state = false; - ::nana::size sz{ wdg_w, essence_->item_size }; + ::nana::size sz{ wdg_w, essence_->scheme_ptr->item_height }; inline_wdg->pane_widget.size(sz); inline_wdg->inline_ptr->resize(sz); @@ -3125,9 +3847,9 @@ namespace nana inline_wdg->indicator->attach(item_pos, inline_wdg); //To reduce the memory usage, the cells may not be allocated - if (item.cells.size() > column_pos) + if (cells.size() > column_pos) { - auto & text = item.cells[column_pos].text; + auto & text = cells[column_pos].text; if (text != inline_wdg->text) { inline_wdg->text = text; @@ -3144,11 +3866,12 @@ namespace nana } } - if (item.cells.size() > column_pos) // process only if the cell is visible + if (cells.size() > column_pos) // process only if the cell is visible { auto cell_txtcolor = fgcolor; - auto & m_cell = item.cells[column_pos]; - nana::size ts = graph->text_extent_size(m_cell.text); // precalcule text geometry + + auto & m_cell = cells[column_pos]; + review_utf8(m_cell.text); if (m_cell.custom_format && (!m_cell.custom_format->bgcolor.invisible())) // adapt to costum format if need { @@ -3158,45 +3881,30 @@ namespace nana if (item_state::highlighted == state) it_bgcolor = it_bgcolor.blend(static_cast(0x99defd), 0.8); - graph->rectangle(rectangle{ item_xpos, y, header.pixels, essence_->item_size }, true, it_bgcolor); + graph->rectangle(rectangle{ column_x, y, col.width_px, essence_->scheme_ptr->item_height }, true, it_bgcolor); cell_txtcolor = m_cell.custom_format->fgcolor; } if (draw_column) { - graph->string(point{ item_xpos + content_pos, y + txtoff }, m_cell.text, cell_txtcolor); // draw full text of the cell index (column) + paint::aligner text_aligner{*graph, col.alignment}; - if (static_cast(ts.width) > static_cast(header.pixels) - (content_pos + item_xpos)) // it was an excess - { - //The text is painted over the next subitem // here beging the ... - int xpos = item_xpos + static_cast(header.pixels) - static_cast(essence_->suspension_width); + unsigned text_margin_right = 0; + if (align::left == col.alignment) + content_pos += essence_->scheme_ptr->text_margin; + else if (align::right == col.alignment) + text_margin_right = essence_->scheme_ptr->text_margin; - // litter rect with the item bg end ... - graph->rectangle(rectangle{ xpos, y + 2, essence_->suspension_width, essence_->item_size - 4 }, true, it_bgcolor); - graph->string(point{ xpos, y + 2 }, L"..."); - - //Erase the part that over the next subitem only if the right of column is less than right of listbox - if (item_xpos + content_pos < content_r.right() - static_cast(header.pixels)) - { - graph->palette(false, bgcolor); // we need to erase the excess, because some cell may not draw text over - graph->rectangle(rectangle{ item_xpos + static_cast(header.pixels), y + 2, - ts.width + static_cast(content_pos)-header.pixels, essence_->item_size - 4 }, true); - } - extreme_text = (std::max)(extreme_text, item_xpos + content_pos + ts.width); - } + graph->palette(true, cell_txtcolor); + text_aligner.draw(m_cell.text, { column_x + content_pos, y + txtoff }, col.width_px - content_pos - text_margin_right); } } - graph->line({ item_xpos - 1, y }, { item_xpos - 1, y + static_cast(essence_->item_size) - 1 }, static_cast(0xEBF4F9)); - + graph->line({ column_x - 1, y }, { column_x - 1, y + static_cast(essence_->scheme_ptr->item_height) - 1 }, static_cast(0xEBF4F9)); } - item_xpos += static_cast(header.pixels); - if (display_order + 1 >= seqs.size() && static_cast(extreme_text) > item_xpos) - { - graph->rectangle(rectangle{item_xpos , y + 2, extreme_text - item_xpos, essence_->item_size - 4}, true, item.bgcolor); - } + column_x += col.width_px; } //Draw selecting inner rectangle @@ -3204,7 +3912,7 @@ namespace nana _m_draw_border(content_r.x, y, show_w); } - essence_t::inline_pane * _m_get_inline_pane(const category_t& cat, std::size_t column_pos) const + essence::inline_pane * _m_get_inline_pane(const category_t& cat, std::size_t column_pos) const { if (column_pos < cat.factories.size()) { @@ -3215,7 +3923,7 @@ namespace nana return nullptr; } - essence_t::inline_pane* _m_find_inline_pane(const index_pair& pos, std::size_t column_pos) const + essence::inline_pane* _m_find_inline_pane(const index_pair& pos, std::size_t column_pos) const { auto & cat = *essence_->lister.get(pos.cat); @@ -3242,22 +3950,26 @@ namespace nana { //Draw selecting inner rectangle auto graph = essence_->graph; - graph->rectangle({ x, y, width, essence_->item_size }, false, static_cast(0x99defd)); - graph->rectangle({ x + 1, y + 1, width - 2, essence_->item_size - 2 }, false, colors::white); + graph->rectangle({ x, y, width, essence_->scheme_ptr->item_height }, false, static_cast(0x99defd)); + graph->rectangle({ x + 1, y + 1, width - 2, essence_->scheme_ptr->item_height - 2 }, false, colors::white); + + const int right = x + width - 1; + const int bottom = y + essence_->scheme_ptr->item_height - 1; + graph->set_pixel(x, y); - graph->set_pixel(x, y + essence_->item_size - 1); - graph->set_pixel(x + width - 1, y); - graph->set_pixel(x + width - 1, y + essence_->item_size - 1); + graph->set_pixel(x, bottom); + graph->set_pixel(right, y); + graph->set_pixel(right, bottom); } private: - essence_t * essence_; + essence * const essence_; mutable facade crook_renderer_; }; //class trigger: public drawer_trigger trigger::trigger() - : essence_(new essence_t), + : essence_(new essence), drawer_header_(new drawer_header_impl(essence_)), drawer_lister_(new drawer_lister_impl(essence_)) {} @@ -3269,7 +3981,7 @@ namespace nana delete essence_; } - essence_t& trigger::essence() const + essence& trigger::ess() const { return *essence_; } @@ -3280,43 +3992,50 @@ namespace nana return; auto & graph = *essence_->graph; - auto size = graph.size(); + + int right = static_cast(graph.width()) - 1; + int bottom = static_cast(graph.height()) - 1; + //Draw Border graph.rectangle(false, static_cast(0x9cb6c5)); - graph.line({ 1, 1 }, {1, static_cast(size.height) - 2}, colors::white); - graph.line({ static_cast(size.width) - 2, 1 }, { static_cast(size.width) - 2, static_cast(size.height) - 2 }); + + graph.line({ 1, 1 }, { 1, bottom - 1}, colors::white); + graph.line({ right - 1, 1 }, { right - 1, bottom - 1 }); if ((essence_->scroll.h.empty() == false) && (essence_->scroll.v.empty() == false)) - graph.rectangle({ static_cast(size.width - 1 - essence_->scroll.scale), - static_cast(size.height - 1 - essence_->scroll.scale), - essence_->scroll.scale, - essence_->scroll.scale }, - true, colors::button_face); + graph.rectangle({ right - static_cast(essence_->scroll.scale), + bottom - static_cast(essence_->scroll.scale), + essence_->scroll.scale, + essence_->scroll.scale }, + true, colors::button_face); } void trigger::attached(widget_reference widget, graph_reference graph) { + essence_->listbox_ptr = static_cast(&widget); essence_->scheme_ptr = static_cast<::nana::listbox::scheme_type*>(API::dev::get_scheme(widget)); essence_->graph = &graph; typeface_changed(graph); essence_->lister.bind(essence_, widget); widget.bgcolor(colors::white); + } void trigger::detached() { essence_->graph = nullptr; + essence_->listbox_ptr = nullptr; } void trigger::typeface_changed(graph_reference graph) { - essence_->text_height = graph.text_extent_size(L"jHWn0123456789/item_size = essence_->text_height + 6; - essence_->suspension_width = graph.text_extent_size(L"...").width; + essence_->scheme_ptr->text_height = graph.text_extent_size(L"jHWn0123456789/scheme_ptr->item_height = essence_->scheme_ptr->text_height + essence_->scheme_ptr->item_height_ex; + essence_->scheme_ptr->suspension_width = graph.text_extent_size("...").width; } - void trigger::refresh(graph_reference) + void trigger::refresh(graph_reference graph) { if (API::is_destroying(essence_->lister.wd_ptr()->handle())) return; @@ -3324,7 +4043,7 @@ namespace nana nana::rectangle r; if (essence_->header.visible() && essence_->rect_header(r)) - drawer_header_->draw(r); + drawer_header_->draw(graph, r); if (essence_->rect_lister(r)) drawer_lister_->draw(r); _m_draw_border(); @@ -3332,73 +4051,74 @@ namespace nana void trigger::mouse_move(graph_reference graph, const arg_mouse& arg) { - using item_state = essence_t::item_state; - using parts = essence_t::parts; - int update = 0; //0 = nothing, 1 = update, 2 = refresh + using item_state = essence::item_state; + using parts = essence::parts; + + bool need_refresh = false; + if(essence_->ptr_state == item_state::pressed) { if(essence_->pointer_where.first == parts::header) - { + { // moving a pressed header : grab it essence_->ptr_state = item_state::grabbed; nana::point pos = arg.pos; essence_->widget_to_header(pos); + + //Start to move a header column or resize a header column(depends on item_spliter_) drawer_header_->grab(pos, true); - API::capture_window(essence_->lister.wd_ptr()->handle(), true); - update = 2; + + essence_->lister.wd_ptr()->set_capture(true); + need_refresh = true; } } if(essence_->ptr_state == item_state::grabbed) - { + { // moving a grabbed header nana::point pos = arg.pos; essence_->widget_to_header(pos); - - nana::rectangle r; - essence_->rect_header(r); - update = drawer_header_->grab_move(r, pos); + need_refresh = drawer_header_->grab_move(pos); } - else if(essence_->calc_where(arg.pos.x, arg.pos.y)) + else if(essence_->calc_where(arg.pos)) { essence_->ptr_state = item_state::highlighted; - update = 2; + need_refresh = true; } - bool set_spliter = false; + bool set_splitter = false; if(essence_->pointer_where.first == parts::header) { nana::rectangle r; if(essence_->rect_header(r)) { - if(drawer_header_->mouse_spliter(r, arg.pos.x)) + if(drawer_header_->detect_splitter(r, arg.pos.x)) { - set_spliter = true; + set_splitter = true; essence_->lister.wd_ptr()->cursor(cursor::size_we); } } } - if(set_spliter == false && essence_->ptr_state != item_state::grabbed) + + if((!set_splitter) && (essence_->ptr_state != item_state::grabbed)) { - if((drawer_header_->item_spliter() != npos) || (essence_->lister.wd_ptr()->cursor() == cursor::size_we)) + if((drawer_header_->splitter() != npos) || (essence_->lister.wd_ptr()->cursor() == cursor::size_we)) { essence_->lister.wd_ptr()->cursor(cursor::arrow); - drawer_header_->cancel_spliter(); - update = 2; + drawer_header_->cancel_splitter(); + need_refresh = true; } } - if (update) + if (need_refresh) { - if (2 == update) - refresh(graph); - - API::lazy_refresh(); + refresh(graph); + API::dev::lazy_refresh(); } } void trigger::mouse_leave(graph_reference graph, const arg_mouse&) { - using item_state = essence_t::item_state; - using parts = essence_t::parts; + using item_state = essence::item_state; + using parts = essence::parts; if((essence_->pointer_where.first != parts::unknown) || (essence_->ptr_state != item_state::normal)) { if (essence_->ptr_state != item_state::grabbed) @@ -3408,23 +4128,23 @@ namespace nana } refresh(graph); - API::lazy_refresh(); + API::dev::lazy_refresh(); } } - void trigger::mouse_down(graph_reference, const arg_mouse& arg) + void trigger::mouse_down(graph_reference graph, const arg_mouse& arg) { - using item_state = essence_t::item_state; - using parts = essence_t::parts; + using item_state = essence::item_state; + using parts = essence::parts; bool update = false; auto & ptr_where = essence_->pointer_where; - if((ptr_where.first == parts::header) && (ptr_where.second != npos || (drawer_header_->item_spliter() != npos))) + if((ptr_where.first == parts::header) && (ptr_where.second != npos || (drawer_header_->splitter() != npos))) { essence_->ptr_state = item_state::pressed; nana::rectangle r; if(essence_->rect_header(r)) { - drawer_header_->draw(r); + drawer_header_->draw(graph, r); update = true; } } @@ -3435,6 +4155,9 @@ namespace nana if (lister.forward(essence_->scroll.offset_y_dpl, ptr_where.second, item_pos)) { auto * item_ptr = (item_pos.is_item() ? &lister.at(item_pos) : nullptr); + + const index_pair abs_item_pos{ item_pos.cat, lister.absolute(item_pos) }; + if(ptr_where.first == parts::lister) { bool sel = true; @@ -3443,28 +4166,32 @@ namespace nana if (arg.shift) lister.select_display_range(lister.last_selected_abs , item_pos, sel); else if (arg.ctrl) - sel = !item_proxy(essence_, index_pair (item_pos.cat, lister.absolute(item_pos))).selected(); + sel = !item_proxy(essence_, abs_item_pos).selected(); else lister.select_for_all(false); //cancel all selections } else - sel = !item_proxy(essence_, index_pair (item_pos.cat, lister.absolute(item_pos))).selected(); + { + //Clicking on a category is ignored when single selection is enabled. + //Fixed by Greentwip(issue #121) + if (item_ptr) + sel = !item_proxy(essence_, abs_item_pos).selected(); + } if(item_ptr) { item_ptr->flags.selected = sel; - index_pair last_selected(item_pos.cat, lister.absolute(item_pos)); - arg_listbox arg{item_proxy{essence_, last_selected}, sel}; - lister.wd_ptr()->events().selected.emit(arg); + arg_listbox arg{ item_proxy{ essence_, abs_item_pos } }; + lister.wd_ptr()->events().selected.emit(arg, lister.wd_ptr()->handle()); if (item_ptr->flags.selected) { - lister.cancel_others_if_single_enabled(true, last_selected); - essence_->lister.last_selected_abs = last_selected; + lister.cancel_others_if_single_enabled(true, abs_item_pos); + essence_->lister.last_selected_abs = abs_item_pos; } - else if (essence_->lister.last_selected_abs == last_selected) + else if (essence_->lister.last_selected_abs == abs_item_pos) essence_->lister.last_selected_abs.set_both(npos); } else if(!lister.single_selection()) @@ -3476,12 +4203,11 @@ namespace nana { item_ptr->flags.checked = ! item_ptr->flags.checked; - index_pair abs_pos{ item_pos.cat, lister.absolute(item_pos) }; - arg_listbox arg{ item_proxy{ essence_, abs_pos }, item_ptr->flags.checked }; - lister.wd_ptr()->events().checked.emit(arg); + arg_listbox arg{ item_proxy{ essence_, abs_item_pos } }; + lister.wd_ptr()->events().checked.emit(arg, lister.wd_ptr()->handle()); if (item_ptr->flags.checked) - lister.cancel_others_if_single_enabled(false, abs_pos); + lister.cancel_others_if_single_enabled(false, abs_item_pos); } else if (! lister.single_check()) lister.categ_checked_reverse(item_pos.cat); @@ -3503,15 +4229,15 @@ namespace nana if(update) { _m_draw_border(); - API::lazy_refresh(); + API::dev::lazy_refresh(); } } void trigger::mouse_up(graph_reference graph, const arg_mouse& arg) { - using item_state = essence_t::item_state; - using parts = essence_t::parts; + using item_state = essence::item_state; + using parts = essence::parts; auto prev_state = essence_->ptr_state; essence_->ptr_state = item_state::highlighted; @@ -3522,9 +4248,9 @@ namespace nana { if(essence_->lister.sort_index(essence_->pointer_where.second)) { - essence_->trace_item_dpl({0,0}); + essence_->trace_item_dpl(index_pair{0,0}); refresh(graph); - API::lazy_refresh(); + API::dev::lazy_refresh(); } } } @@ -3534,8 +4260,8 @@ namespace nana essence_->widget_to_header(pos); drawer_header_->grab(pos, false); refresh(graph); - API::lazy_refresh(); - API::capture_window(essence_->lister.wd_ptr()->handle(), false); + API::dev::lazy_refresh(); + essence_->lister.wd_ptr()->release_capture(); } } @@ -3545,21 +4271,31 @@ namespace nana { refresh(graph); essence_->adjust_scroll_value(); - API::lazy_refresh(); + API::dev::lazy_refresh(); } } - void trigger::dbl_click(graph_reference graph, const arg_mouse& arg) + void trigger::dbl_click(graph_reference graph, const arg_mouse&) { - if (essence_->pointer_where.first == essence_t::parts::header) - if (cursor::size_we == essence_->lister.wd_ptr()->cursor()) - { - if (essence(). auto_width(drawer_header_->item_spliter() )) // ? in order - essence().update(); - return; - } + using parts = essence::parts; - if (essence_->pointer_where.first != essence_t::parts::lister) + if (parts::header == essence_->pointer_where.first) + { + if (cursor::size_we == essence_->lister.wd_ptr()->cursor()) + { + //adjust the width of column to fit its content. + auto split_pos = drawer_header_->splitter(); + if (split_pos != npos) + { + essence_->header.at(split_pos).fit_content(); + refresh(graph); + API::dev::lazy_refresh(); + } + return; + } + } + + if (parts::lister != essence_->pointer_where.first) return; index_pair item_pos; @@ -3572,9 +4308,10 @@ namespace nana return; arg_listbox_category arg_cat(cat_proxy(essence_, item_pos.cat)); - lister.wd_ptr()->events().category_dbl_click.emit(arg_cat); + lister.wd_ptr()->events().category_dbl_click.emit(arg_cat, lister.wd_ptr()->handle()); - if (!arg_cat.category_change_blocked()){ + if (!arg_cat.block_operation) + { bool do_expand = (lister.expand(item_pos.cat) == false); lister.expand(item_pos.cat, do_expand); @@ -3587,7 +4324,7 @@ namespace nana } essence_->adjust_scroll_life(); refresh(graph); - API::lazy_refresh(); + API::dev::lazy_refresh(); } } } @@ -3596,7 +4333,7 @@ namespace nana { essence_->adjust_scroll_life(); refresh(graph); - API::lazy_refresh(); + API::dev::lazy_refresh(); } void trigger::key_press(graph_reference graph, const arg_keyboard& arg) @@ -3615,81 +4352,77 @@ namespace nana break; case L' ': { - selection s; + index_pairs s; bool ck = ! essence_->lister.item_selected_all_checked(s); for(auto i : s) item_proxy(essence_, i).check(ck); } break; - case keyboard::os_pageup : + case keyboard::os_pageup : up = true; - case keyboard::os_pagedown: - { - auto& scrl = essence_->scroll.v; - if (! scrl.make_page_scroll(!up)) - return; - essence_->lister.select_for_all(false); + case keyboard::os_pagedown: + { + auto& scrl = essence_->scroll.v; + if (! scrl.make_page_scroll(!up)) + return; + essence_->lister.select_for_all(false); - index_pair idx{essence_->scroll_y_dpl()}; - if (!up) - essence_->lister.forward(idx, scrl.range()-1, idx); + index_pair idx{essence_->scroll_y_dpl()}; + if (!up) + essence_->lister.forward(idx, scrl.range()-1, idx); - if (idx.is_item()) - item_proxy::from_display(essence_, idx).select(true); - else - if(!essence_->lister.single_selection()) + if (idx.is_item()) + item_proxy::from_display(essence_, idx).select(true); + else if(!essence_->lister.single_selection()) essence_->lister.categ_selected(idx.cat, true); - essence_->trace_last_selected_item (); + essence_->trace_last_selected_item (); - break; - } - case keyboard::os_home: - { - essence_->lister.select_for_all(false); + break; + } + case keyboard::os_home: + { + essence_->lister.select_for_all(false); - index_pair frst{essence_->lister.first()}; - if (frst.is_item()) - item_proxy::from_display(essence_, frst).select(true); - else - if(!essence_->lister.single_selection()) + index_pair frst{essence_->lister.first()}; + if (frst.is_item()) + item_proxy::from_display(essence_, frst).select(true); + else if(!essence_->lister.single_selection()) essence_->lister.categ_selected(frst.cat, true); - essence_->trace_last_selected_item (); - break; - } - case keyboard::os_end: - essence_->lister.select_for_all(false); - item_proxy::from_display(essence_, essence_->lister.last()).select(true); - essence_->trace_last_selected_item (); - break; - + essence_->trace_last_selected_item (); + break; + } + case keyboard::os_end: + essence_->lister.select_for_all(false); + item_proxy::from_display(essence_, essence_->lister.last()).select(true); + essence_->trace_last_selected_item (); + break; default: return; } refresh(graph); - API::lazy_refresh(); + API::dev::lazy_refresh(); } void trigger::key_char(graph_reference graph, const arg_keyboard& arg) { switch(arg.key) { - case keyboard::copy: - { - export_options exp_opt {essence_->def_exp_options}; - exp_opt.columns_order = essence_->header.all_headers(true); - exp_opt.only_selected_items = true; - ::nana::system::dataexch().set(essence_->to_string(exp_opt)); - return; - } - case keyboard::select_all : - essence_->lister.select_for_all(true); + case keyboard::copy: + { + export_options exp_opt {essence_->def_exp_options}; + exp_opt.columns_order = essence_->header.all_headers(true); + exp_opt.only_selected_items = true; + ::nana::system::dataexch().set(essence_->to_string(exp_opt)); + return; + } + case keyboard::select_all : + essence_->lister.select_for_all(true); refresh(graph); - API::lazy_refresh(); + API::dev::lazy_refresh(); break; - default: return; } @@ -3698,38 +4431,38 @@ namespace nana //end class trigger //class item_proxy - item_proxy::item_proxy(essence_t * ess) + + item_proxy::item_proxy(essence * ess) : ess_(ess) {} - item_proxy::item_proxy(essence_t * ess, const index_pair& pos) + item_proxy::item_proxy(essence * ess, const index_pair& pos) : ess_(ess), pos_(pos) { + //get the cat of the item specified by pos if (ess) - { - auto i = ess_->lister.get(pos.cat); - cat_ = &(*i); // what is pos is a cat? - } + cat_ = &(*ess->lister.get(pos.cat)); } - /// the main porpose of this it to make obvious that item_proxy operate with absolute positions, and dont get moved during sort() - item_proxy item_proxy::from_display(essence_t *ess, const index_pair &relative) - { - return item_proxy{ess, ess->lister.absolute_pair(relative)}; - } - item_proxy item_proxy::from_display(const index_pair &relative) const - { - return item_proxy{ess_, ess_->lister.absolute_pair(relative)}; - } + /// the main porpose of this it to make obvious that item_proxy operate with absolute positions, and dont get moved during sort() + item_proxy item_proxy::from_display(essence *ess, const index_pair &relative) + { + return item_proxy{ess, ess->lister.absolute_pair(relative)}; + } - /// posible use: last_selected_display = last_selected.to_display().item; use with caution, it get invalidated after a sort() - index_pair item_proxy::to_display() const - { - return ess_->lister.relative_pair(pos_); - } + item_proxy item_proxy::from_display(const index_pair &relative) const + { + return item_proxy{ess_, ess_->lister.absolute_pair(relative)}; + } - bool item_proxy::empty() const + /// posible use: last_selected_display = last_selected.to_display().item; use with caution, it get invalidated after a sort() + index_pair item_proxy::to_display() const + { + return ess_->lister.relative_pair(pos_); + } + + bool item_proxy::empty() const { return !ess_; } @@ -3740,8 +4473,8 @@ namespace nana if(m.flags.checked != ck) { m.flags.checked = ck; - arg_listbox arg{*this, ck}; - ess_->lister.wd_ptr()->events().checked.emit(arg); + arg_listbox arg{*this}; + ess_->lister.wd_ptr()->events().checked.emit(arg, ess_->lister.wd_ptr()->handle()); ess_->update(); } return *this; @@ -3752,15 +4485,16 @@ namespace nana return cat_->items.at(pos_.item).flags.checked; } - /// is ignored if no change (maybe set last_selected anyway??), but if change emit event, deselect others if need ans set/unset last_selected - item_proxy & item_proxy::select(bool s) + /// is ignored if no change (maybe set last_selected anyway??), but if change emit event, deselect others if need ans set/unset last_selected + item_proxy & item_proxy::select(bool s) { - auto & m = cat_->items.at(pos_.item); // a ref to the real item // what is pos is a cat? + //pos_ never represents a category if this item_proxy is available. + auto & m = cat_->items.at(pos_.item); // a ref to the real item if(m.flags.selected == s) return *this; // ignore if no change m.flags.selected = s; // actually change selection - arg_listbox arg{*this, s}; - ess_->lister.wd_ptr()->events().selected.emit(arg); + arg_listbox arg{*this}; + ess_->lister.wd_ptr()->events().selected.emit(arg, ess_->lister.wd_ptr()->handle()); if (m.flags.selected) { @@ -3770,8 +4504,6 @@ namespace nana else if (ess_->lister.last_selected_abs == pos_) ess_->lister.last_selected_abs.set_both(npos); - ess_->update(); - ess_->update(); return *this; } @@ -3824,7 +4556,7 @@ namespace nana return *this; } - item_proxy& item_proxy::text(size_type col, std::wstring str) + item_proxy& item_proxy::text(size_type col, const std::wstring& str) { ess_->lister.text(cat_, pos_.item, col, to_utf8(str), columns()); ess_->update(); @@ -3833,6 +4565,14 @@ namespace nana std::string item_proxy::text(size_type col) const { + if (cat_->model_ptr) + { + auto cells = cat_->model_ptr->container()->to_cells(pos_.item); + if (col < cells.size()) + return cells[col].text; + + return{}; + } return ess_->lister.get_cells(cat_, pos_.item).at(col).text; } @@ -3942,7 +4682,7 @@ namespace nana } //Undocumented methods - essence_t * item_proxy::_m_ess() const + essence * item_proxy::_m_ess() const { return ess_; } @@ -3969,15 +4709,16 @@ namespace nana //end class item_proxy //class cat_proxy + //the member cat_ is used for fast accessing to the category - cat_proxy::cat_proxy(essence_t * ess, size_type pos) + cat_proxy::cat_proxy(essence * ess, size_type pos) : ess_(ess), pos_(pos) { _m_cat_by_pos(); } - cat_proxy::cat_proxy(essence_t* ess, category_t* cat) + cat_proxy::cat_proxy(essence* ess, category_t* cat) : ess_(ess), cat_(cat) { @@ -3989,6 +4730,14 @@ namespace nana } } + model_guard cat_proxy::model() + { + if (!cat_->model_ptr) + throw std::runtime_error("nana::listbox has not a model for the category"); + + return{ cat_->model_ptr.get() }; + } + void cat_proxy::append(std::initializer_list arg) { const auto items = columns(); @@ -4011,7 +4760,7 @@ namespace nana size_type pos = 0; for (auto & txt : arg) { - ip.text(pos++, to_utf8(txt)); + ip.text(pos++, txt); if (pos >= items) break; } @@ -4069,21 +4818,46 @@ namespace nana return to_utf8(cat_->text); } + bool assign_colors_for_last(essence* ess, category_t* cat) + { + auto wd = ess->lister.wd_ptr(); + if (wd && !API::empty_window(wd->handle())) + { + auto & m = cat->items.back(); + m.bgcolor = wd->bgcolor(); + m.fgcolor = wd->fgcolor(); + + return true; + } + return false; + } + void cat_proxy::push_back(std::string s) { internal_scope_guard lock; - cat_->sorted.push_back(cat_->items.size()); - cat_->items.emplace_back(std::move(s)); + ess_->lister.throw_if_immutable_model(index_pair{ pos_ }); - auto wd = ess_->lister.wd_ptr(); - if(wd && !(API::empty_window(wd->handle()))) + cat_->sorted.push_back(cat_->items.size()); + + if (cat_->model_ptr) { - auto & m = cat_->items.back(); - m.bgcolor = wd->bgcolor(); - m.fgcolor = wd->fgcolor(); - ess_->update(); + auto pos = cat_->model_ptr->container()->size(); + cat_->model_ptr->container()->emplace_back(); + auto cells = cat_->model_ptr->container()->to_cells(pos); + if (cells.size()) + cells.front().text.swap(s); + else + cells.emplace_back(std::move(s)); + + cat_->model_ptr->container()->assign(pos, cells); + cat_->items.emplace_back(); } + else + cat_->items.emplace_back(std::move(s)); + + if (assign_colors_for_last(ess_, cat_)) + ess_->update(); } //Behavior of a container @@ -4257,12 +5031,29 @@ namespace nana cells.resize(columns()); cat_->items.emplace_back(std::move(cells)); - auto wd = ess_->lister.wd_ptr(); - if (wd && !(API::empty_window(wd->handle()))) + assign_colors_for_last(ess_, cat_); + } + + void cat_proxy::_m_try_append_model(const const_virtual_pointer& dptr) + { + if (!cat_->model_ptr) { - auto & m = cat_->items.back(); - m.bgcolor = wd->bgcolor(); - m.fgcolor = wd->fgcolor(); + //Throws when appends an object to a listbox which should have a model. + throw std::runtime_error("nana::listbox hasn't a model"); + } + + ess_->lister.throw_if_immutable_model(cat_->model_ptr.get()); + + if (cat_->model_ptr->container()->push_back(dptr)) + { + cat_->sorted.push_back(cat_->items.size()); + cat_->items.emplace_back(); + + assign_colors_for_last(ess_, cat_); + } + else + { + throw std::invalid_argument("nana::listbox, the type of operand object is mismatched with model container value_type"); } } @@ -4281,41 +5072,51 @@ namespace nana //A fix for auto_draw, to make sure the inline widget set() issued after value() and value_ptr() are actually set. //Fixed by leobackes(pr#86) - void cat_proxy::_m_update() { + void cat_proxy::_m_update() + { ess_->update(); } - //class cat_proxy + void cat_proxy::_m_reset_model(model_interface* p) + { + if (ess_->listbox_ptr) + { + cat_->model_ptr.reset(p); + cat_->items.clear(); + cat_->sorted.clear(); + cat_->items.resize(cat_->model_ptr->container()->size()); + + const auto item_size = cat_->items.size(); + cat_->sorted.reserve(item_size + 100); + for (std::size_t pos = 0; pos != item_size; ++pos) + cat_->sorted.push_back(pos); + + ess_->lister.sort(); + + ess_->adjust_scroll_life(); + API::refresh_window(ess_->listbox_ptr->handle()); + } + } //end class cat_proxy } }//end namespace drawerbase - arg_listbox::arg_listbox(const drawerbase::listbox::item_proxy& m, bool selected) noexcept - : item(m), selected(selected) + arg_listbox::arg_listbox(const drawerbase::listbox::item_proxy& m) noexcept + : item(m) { } - //Implementation of arg_category + //Implementation of arg_listbox_category //Contributed by leobackes(pr#97) arg_listbox_category::arg_listbox_category(const nana::drawerbase::listbox::cat_proxy& cat) noexcept - : category(cat), block_change_(false) + : category(cat) { } - void arg_listbox_category::block_category_change() const noexcept - { - block_change_ = true; - } - - bool arg_listbox_category::category_change_blocked() const noexcept - { - return block_change_; - } - - //class listbox + listbox::listbox(window wd, bool visible) { create(wd, rectangle(), visible); @@ -4326,6 +5127,17 @@ namespace nana create(wd, r, visible); } + bool listbox::assoc_ordered(bool enable) + { + internal_scope_guard lock; + + auto & ess = _m_ess(); + if (ess.lister.enable_ordered(enable)) + ess.update(); + + return true; + } + void listbox::auto_draw(bool ad) { _m_ess().set_auto_draw(ad); @@ -4371,7 +5183,7 @@ namespace nana { internal_scope_guard lock; auto & ess = _m_ess(); - auto pos = ess.header.create(to_nstring(std::move(s)), width); + auto pos = ess.header.create(&ess, to_nstring(std::move(s)), width); ess.update(); return pos; } @@ -4380,31 +5192,11 @@ namespace nana { internal_scope_guard lock; auto & ess = _m_ess(); - auto pos = ess.header.create(to_nstring(std::move(s)), width); + auto pos = ess.header.create(&ess, to_nstring(std::move(s)), width); ess.update(); return pos; } - listbox& listbox::header_width(size_type pos, unsigned pixels) - { - auto & ess = _m_ess(); - ess.header.item_width(pos, pixels); - ess.update(); - return *this; - } - unsigned listbox::auto_width(size_type pos, unsigned max) - { - auto & ess = _m_ess(); - unsigned max_w = ess.auto_width(pos, max); - ess.update(); - return max_w; - } - - unsigned listbox::header_width(size_type pos) const - { - return _m_ess().header.item_width(pos); - } - listbox::cat_proxy listbox::append(std::string s) { internal_scope_guard lock; @@ -4460,74 +5252,89 @@ namespace nana return cat_proxy{ &ess, new_cat_ptr }; } - listbox::cat_proxy listbox::at(size_type pos) const + + void listbox::insert_item(const index_pair& pos, std::string text) + { + internal_scope_guard lock; + auto & ess = _m_ess(); + ess.lister.insert(pos, std::move(text), ess.header.cont().size()); + + if (!empty()) + { + auto & item = ess.lister.at(pos); + item.bgcolor = bgcolor(); + item.fgcolor = fgcolor(); + ess.update(); + } + } + + void listbox::insert_item(const index_pair& pos, const std::wstring& text) + { + insert_item(pos, to_utf8(text)); + } + + listbox::cat_proxy listbox::at(size_type pos) + { + auto & ess = _m_ess(); + if (pos >= ess.lister.size_categ()) + throw std::out_of_range("Nana.Listbox.at(): invalid position"); + + return{ &ess, pos }; + } + + const listbox::cat_proxy listbox::at(size_type pos) const { auto & ess = _m_ess(); if(pos >= ess.lister.size_categ()) throw std::out_of_range("Nana.Listbox.at(): invalid position"); - return cat_proxy(&ess, pos); + return{ &ess, pos }; } - listbox& listbox::ordered_categories(bool enable_ordered) + listbox::item_proxy listbox::at(const index_pair& abs_pos) { - internal_scope_guard lock; - - auto & ess = _m_ess(); - if (ess.lister.enable_ordered(enable_ordered)) - ess.update(); - - return *this; + return at(abs_pos.cat).at(abs_pos.item); } - listbox::item_proxy listbox::at(const index_pair& pos_abs) const + const listbox::item_proxy listbox::at(const index_pair& pos_abs) const { return at(pos_abs.cat).at(pos_abs.item); } - // Contributed by leobackes(pr#97) - listbox::index_pair listbox::at ( const point& pos ) const - { - auto & ess=_m_ess(); - auto _where=ess.where(pos.x, pos.y); - index_pair item_pos{npos,npos}; - if(_where.first==drawerbase::listbox::essence_t::parts::lister){ - auto & offset_y = ess.scroll.offset_y_dpl; - ess.lister.forward(offset_y, _where.second, item_pos); - } - return item_pos; - } - - //Contributed by leobackes(pr#97) - listbox::columns_indexs listbox::column_from_pos ( const point& pos ) - { - auto & ess=_m_ess(); - columns_indexs col=ess.header.item_by_x(pos.x - 2 - ess.scroll.offset_x); - return col; - } - - - - void listbox::insert(const index_pair& pos, std::string text) + listbox::index_pair listbox::cast( const point& pos ) const { - internal_scope_guard lock; - auto & ess = _m_ess(); - if (ess.lister.insert(pos, std::move(text))) + auto & ess=_m_ess(); + auto _where=ess.where(pos.x, pos.y); + index_pair item_pos{npos,npos}; + if(_where.first==drawerbase::listbox::essence::parts::lister) { - if (!empty()) - { - auto & item = ess.lister.at(pos); - item.bgcolor = bgcolor(); - item.fgcolor = fgcolor(); - ess.update(); - } + auto & offset_y = ess.scroll.offset_y_dpl; + ess.lister.forward(offset_y, _where.second, item_pos); } + return item_pos; } - void listbox::insert(const index_pair& pos, std::wstring text) + auto listbox::column_at(size_type pos) -> column_interface& { - insert(pos, to_utf8(text)); + return _m_ess().header.at(pos); + } + + auto listbox::column_at(size_type pos) const -> const column_interface& + { + return _m_ess().header.at(pos); + } + + auto listbox::column_size() const ->size_type + { + return _m_ess().header.cont().size(); + } + + //Contributed by leobackes(pr#97) + listbox::size_type listbox::column_from_pos ( const point& pos ) + { + auto & ess=_m_ess(); + return ess.header.column_from_point(pos.x - 2 - static_cast(ess.scroll.x_offset())); } void listbox::checkable(bool chkable) @@ -4540,20 +5347,19 @@ namespace nana } } - auto listbox::checked() const -> selection + auto listbox::checked() const -> index_pairs { - return _m_ess().lister.item_checked(); + return _m_ess().lister.pick_items(false); } void listbox::clear(size_type cat) { auto & ess = _m_ess(); ess.lister.clear(cat); - //unsort(); // ?? - // from current display position - // move to the cat self if not in first cat - // move to first item ?? if in first cat + // from current display position + // move to the cat self if not in first cat + // move to first item ?? if in first cat ess.scroll_y_abs(ess.scroll_y_abs()); ess.update(); @@ -4650,7 +5456,7 @@ namespace nana void listbox::set_sort_compare(size_type col, std::function strick_ordering) { - _m_ess().header.column(col).weak_ordering = std::move(strick_ordering); + _m_ess().header.at(col).weak_ordering = std::move(strick_ordering); } /// sort() and ivalidate any existing reference from display position to absolute item, that is: after sort() display offset point to different items @@ -4675,11 +5481,9 @@ namespace nana return !_m_ess().lister.active_sort(!freeze); } - auto listbox::selected() const -> selection // change to: selection selected(); + auto listbox::selected() const -> index_pairs { - selection s; - _m_ess().lister.item_selected(s); // absolute positions, no relative to display - return std::move(s); + return _m_ess().lister.pick_items(true); // absolute positions, no relative to display } void listbox::show_header(bool sh) @@ -4706,11 +5510,6 @@ namespace nana return _m_ess().lister.size_categ(); } - listbox::size_type listbox::size_item() const - { - return size_item(0); - } - listbox::size_type listbox::size_item(size_type categ) const { return _m_ess().lister.size_item(categ); @@ -4732,9 +5531,9 @@ namespace nana return _m_ess().def_exp_options; } - drawerbase::listbox::essence_t & listbox::_m_ess() const + drawerbase::listbox::essence & listbox::_m_ess() const { - return get_drawer_trigger().essence(); + return get_drawer_trigger().ess(); } nana::any* listbox::_m_anyobj(size_type cat, size_type index, bool allocate_if_empty) const @@ -4742,7 +5541,7 @@ namespace nana return _m_ess().lister.anyobj(index_pair{cat, index}, allocate_if_empty); } - drawerbase::listbox::category_t* listbox::_m_at_key(std::shared_ptr ptr) + drawerbase::listbox::category_t* listbox::_m_assoc(std::shared_ptr ptr, bool create_if_not_exists) { auto & ess = _m_ess(); @@ -4750,10 +5549,13 @@ namespace nana for (auto & m : ess.lister.cat_container()) { - if (m.key_ptr && nana::detail::pred_equal_by_less(ptr.get(), m.key_ptr.get())) + if (m.key_ptr && nana::detail::pred_equal(ptr.get(), m.key_ptr.get())) return &m; } + if (!create_if_not_exists) + return nullptr; + drawerbase::listbox::category_t* cat; if (ess.lister.enable_ordered()) @@ -4776,7 +5578,7 @@ namespace nana internal_scope_guard lock; for (auto i = cont.begin(); i != cont.end(); ++i) { - if (i->key_ptr && nana::detail::pred_equal_by_less(p, i->key_ptr.get())) + if (i->key_ptr && nana::detail::pred_equal(p, i->key_ptr.get())) { cont.erase(i); return; diff --git a/source/gui/widgets/menu.cpp b/source/gui/widgets/menu.cpp index 53930ae4..2ba13a4d 100644 --- a/source/gui/widgets/menu.cpp +++ b/source/gui/widgets/menu.cpp @@ -350,7 +350,7 @@ namespace nana if(track_mouse(arg.pos)) { refresh(graph); - API::lazy_refresh(); + API::dev::lazy_refresh(); } } @@ -398,24 +398,28 @@ namespace nana //Draw text, the text is transformed from orignal for hotkey character wchar_t hotkey; std::string::size_type hotkey_pos; - auto text = to_wstring(API::transform_shortkey_text(m.text, hotkey, &hotkey_pos)); + auto text = API::transform_shortkey_text(m.text, hotkey, &hotkey_pos); if (m.image.empty() == false) renderer->item_image(graph, nana::point(item_r.x + 5, item_r.y + static_cast(item_h_px - image_px) / 2 - 1), image_px, m.image); - renderer->item_text(graph, nana::point(item_r.x + 40, item_r.y + text_top_off), to_utf8(text), strpixels, attr); + renderer->item_text(graph, nana::point(item_r.x + 40, item_r.y + text_top_off), text, strpixels, attr); if (hotkey) { m.hotkey = hotkey; if (m.flags.enabled) { - unsigned off_w = (hotkey_pos ? graph.text_extent_size(text, static_cast(hotkey_pos)).width : 0); - nana::size hotkey_size = graph.text_extent_size(text.c_str() + hotkey_pos, 1); - int x = item_r.x + 40 + off_w; - int y = item_r.y + text_top_off + hotkey_size.height; + auto off_px = (hotkey_pos ? graph.text_extent_size(text.c_str(), hotkey_pos).width : 0); + auto hotkey_px = graph.text_extent_size(text.c_str() + hotkey_pos, 1).width; - graph_->line({ x, y }, { x + static_cast(hotkey_size.width) - 1, y }, colors::black); + unsigned ascent, descent, inleading; + graph.text_metrics(ascent, descent, inleading); + + int x = item_r.x + 40 + off_px; + int y = item_r.y + text_top_off + ascent + 1; + + graph_->line({ x, y }, { x + static_cast(hotkey_px)-1, y }, colors::black); } } @@ -582,9 +586,12 @@ namespace nana } else if(m.flags.enabled) { - std::move(fn_close_tree_)(); - item_proxy ip(index, m); - m.functor.operator()(ip); + fn_close_tree_(); + if (m.functor) + { + item_proxy ip(index, m); + m.functor.operator()(ip); + } return 1; } } @@ -1230,14 +1237,14 @@ namespace nana return impl_->mbuilder.data().items.at(index).flags.checked; } - void menu::answerer(std::size_t index, const menu::event_fn_t& fn) + void menu::answerer(std::size_t index, const event_fn_t& fn) { impl_->mbuilder.data().items.at(index).functor = fn; } - void menu::destroy_answer(const std::function& f) + void menu::destroy_answer(const std::function& fn) { - impl_->destroy_answer = f; + impl_->destroy_answer = fn; } void menu::gaps(const nana::point& pos) @@ -1363,24 +1370,8 @@ namespace nana return; } } - bool popup = false; - switch(mouse_) - { - case mouse::left_button: - popup = arg.left_button; - break; - case mouse::middle_button: - popup = arg.mid_button; - break; - case mouse::right_button: - popup = arg.right_button; - break; - case mouse::any_button: - popup = true; - default: - break; - } - if(popup) + + if((mouse::any_button == mouse_) || (mouse_ == arg.button)) mobj_.popup(owner_, pos_.x, pos_.y); } //end class diff --git a/source/gui/widgets/menubar.cpp b/source/gui/widgets/menubar.cpp index 459b61fb..10bf8814 100644 --- a/source/gui/widgets/menubar.cpp +++ b/source/gui/widgets/menubar.cpp @@ -253,7 +253,7 @@ namespace nana { _m_popup_menu(); refresh(graph); - API::lazy_refresh(); + API::dev::lazy_refresh(); } state_.mouse_pos = arg.pos; @@ -281,7 +281,7 @@ namespace nana _m_total_close(); refresh(graph); - API::lazy_refresh(); + API::dev::lazy_refresh(); } void trigger::mouse_up(graph_reference graph, const arg_mouse&) @@ -298,7 +298,7 @@ namespace nana state_.behavior = state_.behavior_none; _m_total_close(); refresh(graph); - API::lazy_refresh(); + API::dev::lazy_refresh(); } } @@ -312,7 +312,7 @@ namespace nana _m_close_menu(); state_.active = npos; refresh(graph); - API::lazy_refresh(); + API::dev::lazy_refresh(); } } @@ -407,7 +407,7 @@ namespace nana } refresh(graph); - API::lazy_refresh(); + API::dev::lazy_refresh(); } void trigger::key_release(graph_reference graph, const arg_keyboard& arg) @@ -429,7 +429,7 @@ namespace nana state_.menu_active = false; refresh(graph); - API::lazy_refresh(); + API::dev::lazy_refresh(); } } @@ -449,7 +449,7 @@ namespace nana state_.menu->goto_next(true); refresh(graph); - API::lazy_refresh(); + API::dev::lazy_refresh(); state_.behavior = state_.behavior_menu; } } @@ -477,7 +477,7 @@ namespace nana { state_.active = index; refresh(*graph_); - API::lazy_refresh(); + API::dev::lazy_refresh(); if(_m_popup_menu()) state_.menu->goto_next(true); @@ -610,7 +610,7 @@ namespace nana ::create(wd, rectangle(nana::size(API::window_size(wd).width, 28))); API::dev::set_menubar(handle(), true); - evt_resized_ = API::events(wd).resized([this](const ::nana::arg_resized& arg) + evt_resized_ = API::events(wd).resized.connect([this](const ::nana::arg_resized& arg) { auto sz = this->size(); sz.width = arg.width; diff --git a/source/gui/widgets/picture.cpp b/source/gui/widgets/picture.cpp index 9733e120..bf739693 100644 --- a/source/gui/widgets/picture.cpp +++ b/source/gui/widgets/picture.cpp @@ -76,7 +76,7 @@ namespace nana { auto valid_area = backimg.valid_area; if (valid_area.empty()) - valid_area = backimg.image.size(); + valid_area.dimension(backimg.image.size()); if (backimg.stretchable) { diff --git a/source/gui/widgets/skeletons/text_editor.cpp b/source/gui/widgets/skeletons/text_editor.cpp index fc6086cc..03168ce1 100644 --- a/source/gui/widgets/skeletons/text_editor.cpp +++ b/source/gui/widgets/skeletons/text_editor.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include namespace nana{ namespace widgets @@ -39,8 +38,9 @@ namespace nana{ namespace widgets void set_selected_text() { - if (editor_._m_get_sort_select_points(sel_a_, sel_b_)) - editor_._m_make_select_string(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; } void set_caret_pos() @@ -53,7 +53,7 @@ namespace nana{ namespace widgets return cmd_; } - virtual bool merge(const undoable_command_interface& rhs) override + virtual bool merge(const undoable_command_interface&) override { return false; } @@ -217,14 +217,25 @@ namespace nana{ namespace widgets 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_; + editor_.points_.caret = (sel_a_ < sel_b_ ? sel_a_ : sel_b_); + + const auto text = editor_._m_make_select_string(); + + editor_._m_erase_select(); + editor_._m_put(text); + + editor_.select_.a = sel_a_; + editor_.select_.b = sel_b_; + + editor_.points_.caret = sel_b_; + editor_.reset_caret(); } - editor_._m_move_select(false); } void set_destination(const nana::upoint& dest_a, const nana::upoint& dest_b) @@ -239,7 +250,7 @@ namespace nana{ namespace widgets class text_editor::editor_behavior_interface { public: - virtual ~editor_behavior_interface(){} + virtual ~editor_behavior_interface() = default; /// 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; @@ -273,8 +284,8 @@ namespace nana{ namespace widgets : editor_(editor) {} - void merge_lines(std::size_t first, std::size_t second) override{} - void add_lines(std::size_t pos, std::size_t lines) override{} + void merge_lines(std::size_t, std::size_t) override{} + void add_lines(std::size_t, std::size_t) override{} void pre_calc_line(std::size_t, unsigned) override{} void pre_calc_lines(unsigned) override{} @@ -283,12 +294,12 @@ namespace nana{ namespace widgets return editor_.textbase_.lines(); } - std::size_t take_lines(std::size_t pos) const override + std::size_t take_lines(std::size_t) const override { return 1; } - void update_line(std::size_t textline, std::size_t secondary_before) override + void update_line(std::size_t textline, std::size_t) override { int top = editor_._m_text_top_base() + static_cast(editor_.line_height() * (textline - editor_.points_.offset.y)); editor_.graph_.rectangle({ editor_.text_area_.area.x, top, editor_.text_area_.area.width, editor_.line_height() }, true, API::bgcolor(editor_.window_)); @@ -301,9 +312,7 @@ namespace nana{ namespace widgets ::nana::upoint str_pos(0, static_cast(editor_.points_.offset.y)); - std::size_t scrlines = editor_.screen_lines() + str_pos.y; - if (scrlines > editor_.textbase_.lines()) - scrlines = editor_.textbase_.lines(); + const auto scrlines = (std::min)(editor_.screen_lines() + str_pos.y, static_cast(editor_.textbase_.lines())); int top = editor_._m_text_top_base(); const unsigned pixels = editor_.line_height(); @@ -321,18 +330,18 @@ namespace nana{ namespace widgets nana::point caret_to_screen(nana::upoint pos) override { - auto & textbase = editor_.textbase_; - if (pos.y > static_cast(textbase.lines())) - pos.y = static_cast(textbase.lines()); + pos.y = (std::min)(pos.y, static_cast(editor_.textbase_.lines())); - std::unique_ptr mask_str; + auto text_ptr = &editor_.textbase_.getline(pos.y); + + std::wstring mask_str; if (editor_.mask_char_) - mask_str.reset(new std::wstring(textbase.getline(pos.y).size(), editor_.mask_char_)); + { + mask_str.resize(text_ptr->size(), editor_.mask_char_); + text_ptr = &mask_str; + } - auto & lnstr = editor_.mask_char_ ? *mask_str : textbase.getline(pos.y); - - pos.x = editor_._m_pixels_by_char(lnstr, pos.x) + editor_.text_area_.area.x; - + pos.x = editor_._m_pixels_by_char(*text_ptr, pos.x) + editor_.text_area_.area.x; int pos_y = static_cast((pos.y - editor_.points_.offset.y) * editor_.line_height() + editor_._m_text_top_base()); return{ static_cast(pos.x - editor_.points_.offset.x), pos_y }; @@ -343,21 +352,23 @@ namespace nana{ namespace widgets nana::upoint res{ 0, static_cast(_m_textline_from_screen(scrpos.y)) }; //Convert the screen point to text caret point - const string_type& real_str = editor_.textbase_.getline(res.y); - std::unique_ptr mask_str; + auto text_ptr = &editor_.textbase_.getline(res.y); + + std::wstring mask_str; if (editor_.mask_char_) - mask_str.reset(new std::wstring(real_str.size(), editor_.mask_char_)); - - auto & lnstr = (editor_.mask_char_ ? *mask_str : real_str); - if (lnstr.size() > 0) + { + mask_str.resize(text_ptr->size(), editor_.mask_char_); + text_ptr = &mask_str; + } + + if (text_ptr->size() > 0) { scrpos.x += (editor_.points_.offset.x - editor_.text_area_.area.x); if (scrpos.x > 0) { - unicode_bidi bidi; std::vector reordered; - bidi.linestr(lnstr.data(), lnstr.size(), reordered); + unicode_bidi{}.linestr(text_ptr->c_str(), text_ptr->size(), reordered); for (auto & ent : reordered) { @@ -365,16 +376,14 @@ namespace nana{ namespace widgets auto str_px = static_cast(editor_._m_text_extent_size(ent.begin, len).width); if (scrpos.x < str_px) { - std::unique_ptr pxbuf(new unsigned[len]); - - res.x = editor_._m_char_by_pixels(ent.begin, len, pxbuf.get(), str_px, scrpos.x, is_right_text(ent)); - res.x += static_cast(ent.begin - lnstr.data()); + res.x = editor_._m_char_by_pixels(ent, static_cast(scrpos.x)); + res.x += static_cast(ent.begin - text_ptr->c_str()); return res; } scrpos.x -= str_px; } - res.x = static_cast(lnstr.size()); + res.x = static_cast(text_ptr->size()); } } @@ -389,10 +398,9 @@ namespace nana{ namespace widgets { if (points.caret.y) { - points.caret.x = static_cast(editor_.textbase_.getline(--points.caret.y).size()); - - if (points.xpos < points.caret.x) - points.caret.x = points.xpos; + points.caret.x = (std::min)(points.xpos, + static_cast(editor_.textbase_.getline(--points.caret.y).size()) + ); bool out_of_screen = (static_cast(points.caret.y) < points.offset.y); if (out_of_screen) @@ -405,10 +413,9 @@ namespace nana{ namespace widgets { if (points.caret.y + 1 < editor_.textbase_.lines()) { - points.caret.x = static_cast(editor_.textbase_.getline(++points.caret.y).size()); - - if (points.xpos < points.caret.x) - points.caret.x = points.xpos; + points.caret.x = (std::min)(points.xpos, + static_cast(editor_.textbase_.getline(++points.caret.y).size()) + ); return adjust_caret_into_screen(); } @@ -430,11 +437,8 @@ namespace nana{ namespace widgets editor_._m_get_scrollbar_size(); - auto x = points.caret.x; auto& lnstr = textbase.getline(points.caret.y); - - if (x > lnstr.size()) x = static_cast(lnstr.size()); - + const auto x = (points.caret.x > lnstr.size() ? static_cast(lnstr.size()) : points.caret.x); auto const text_w = editor_._m_pixels_by_char(lnstr, x); unsigned area_w = editor_._m_text_area().width; @@ -477,7 +481,8 @@ namespace nana{ namespace widgets std::size_t _m_textline_from_screen(int y) const { const std::size_t textlines = editor_.textbase_.lines(); - if (0 == textlines) + const auto line_px = static_cast(editor_.line_height()); + if ((0 == textlines) || (0 == line_px)) return 0; const int offset_top = editor_.points_.offset.y; @@ -486,7 +491,7 @@ namespace nana{ namespace widgets if (y < text_area_top) y = offset_top ? offset_top - 1 : 0; else - y = (y - text_area_top) / static_cast(editor_.line_height()) + offset_top; + y = (y - text_area_top) / line_px + offset_top; return (textlines <= static_cast(y) ? textlines - 1 : static_cast(y)); } @@ -504,13 +509,8 @@ namespace nana{ namespace widgets const wchar_t* end; unsigned pixels; - text_section() - { - throw std::runtime_error("text_section default construction is forbidden."); - } - - text_section(const wchar_t* ptr, const wchar_t* endptr) - : begin(ptr), end(endptr) + text_section(const wchar_t* ptr, const wchar_t* endptr, unsigned px) + : begin(ptr), end(endptr), pixels(px) {} }; @@ -520,6 +520,9 @@ namespace nana{ namespace widgets std::vector line_sections; }; public: + /// A coordinate type for line position. first: the absolute line position of text. second: the secondary line position of a part of line. + using row_coordinate = std::pair; + behavior_linewrapped(text_editor& editor) : editor_(editor) {} @@ -541,7 +544,7 @@ namespace nana{ namespace widgets { auto& linestr = editor_.textbase_.getline(line); auto p = mtr.line_sections.front().begin; - if (p < linestr.data() || (linestr.data() + linestr.size() < p)) + if (p < linestr.c_str() || (linestr.c_str() + linestr.size() < p)) pre_calc_line(line, editor_.width_pixels()); ++line; @@ -564,7 +567,7 @@ namespace nana{ namespace widgets { auto & linestr = editor_.textbase_.getline(line); auto p = mtr.line_sections.front().begin; - if (p < linestr.data() || (linestr.data() + linestr.size() < p)) + if (p < linestr.c_str() || (linestr.c_str() + linestr.size() < p)) pre_calc_line(line, editor_.width_pixels()); } ++line; @@ -579,8 +582,8 @@ namespace nana{ namespace widgets { auto & mtr = linemtr_[line]; mtr.line_sections.clear(); - mtr.line_sections.emplace_back(lnstr.data(), lnstr.data()); - mtr.line_sections.back().pixels = 0; + + mtr.line_sections.emplace_back(lnstr.c_str(), lnstr.c_str(), unsigned{}); mtr.take_lines = 1; return; } @@ -604,8 +607,7 @@ namespace nana{ namespace widgets { if (text_px != str_w) { - line_sections.emplace_back(secondary_begin, ts.begin); - line_sections.back().pixels = 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; } @@ -628,8 +630,7 @@ namespace nana{ namespace widgets continue; const wchar_t * endptr = ts.begin + (pxi - pxptr) + (text_px == pixels ? 1 : 0); - line_sections.emplace_back(secondary_begin, endptr); - line_sections.back().pixels = text_px - (text_px == pixels ? 0 : *pxi); + 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); @@ -639,8 +640,7 @@ namespace nana{ namespace widgets } else if (text_px == pixels) { - line_sections.emplace_back(secondary_begin, ts.begin); - line_sections.back().pixels = text_px - str_w; + line_sections.emplace_back(secondary_begin, ts.begin, unsigned{ text_px - str_w }); secondary_begin = ts.begin; text_px = str_w; } @@ -653,8 +653,7 @@ namespace nana{ namespace widgets if (secondary_begin) { - mtr.line_sections.emplace_back(secondary_begin, sections.back().end); - mtr.line_sections.back().pixels = text_px; + mtr.line_sections.emplace_back(secondary_begin, sections.back().end, unsigned{ text_px }); ++mtr.take_lines; } } @@ -692,7 +691,7 @@ namespace nana{ namespace widgets editor_.graph_.rectangle({ editor_.text_area_.area.x, top, editor_.width_pixels(), static_cast(pixels * secondary_before) }, true, API::bgcolor(editor_.window_)); auto fgcolor = API::fgcolor(editor_.window_); - auto text_ptr = editor_.textbase_.getline(textline).data(); + auto text_ptr = editor_.textbase_.getline(textline).c_str(); for (std::size_t pos = 0; pos < secondary_before; ++pos, top+=pixels) { @@ -708,13 +707,12 @@ namespace nana{ namespace widgets { std::vector line_index; - std::size_t secondary; - auto primary = _m_textline_from_screen(0, secondary); - if (primary >= linemtr_.size() || secondary >= linemtr_[primary].line_sections.size()) + auto row = _m_textline_from_screen(0); + if (row.first >= linemtr_.size() || row.second >= linemtr_[row.first].line_sections.size()) return line_index; - nana::upoint str_pos(0, static_cast(primary)); - str_pos.x = static_cast(linemtr_[primary].line_sections[secondary].begin - editor_.textbase_.getline(primary).data()); + nana::upoint str_pos(0, static_cast(row.first)); + str_pos.x = static_cast(linemtr_[row.first].line_sections[row.second].begin - editor_.textbase_.getline(row.first).c_str()); int top = editor_._m_text_top_base(); const unsigned pixels = editor_.line_height(); @@ -722,19 +720,19 @@ namespace nana{ namespace widgets const std::size_t scrlines = editor_.screen_lines(); for (std::size_t pos = 0; pos < scrlines; ++pos, top += pixels) { - if ((primary < linemtr_.size()) && (secondary < linemtr_[primary].line_sections.size())) + if ((row.first < linemtr_.size()) && (row.second < linemtr_[row.first].line_sections.size())) { - auto & mtr = linemtr_[primary]; - auto & section = mtr.line_sections[secondary]; + auto & mtr = linemtr_[row.first]; + auto & section = mtr.line_sections[row.second]; std::wstring text(section.begin, section.end); editor_._m_draw_string(top, fgcolor, str_pos, text, true); line_index.push_back(str_pos); - ++secondary; - if (secondary >= mtr.line_sections.size()) + ++row.second; + if (row.second >= mtr.line_sections.size()) { - ++primary; - secondary = 0; + ++row.first; + row.second = 0; str_pos.x = 0; ++str_pos.y; } @@ -784,7 +782,7 @@ namespace nana{ namespace widgets } else if (pos.x == chsize) { - scrpos.x = editor_._m_text_extent_size(str.data(), sec.end - sec.begin).width; + scrpos.x = editor_._m_text_extent_size(str.c_str(), sec.end - sec.begin).width; break; } else @@ -802,41 +800,40 @@ namespace nana{ namespace widgets nana::upoint screen_to_caret(point scrpos) override { - std::size_t secondary; - std::size_t primary = _m_textline_from_screen(scrpos.y, secondary); + const auto row = _m_textline_from_screen(scrpos.y); - auto & mtr = linemtr_[primary]; + auto & mtr = linemtr_[row.first]; if (mtr.line_sections.empty()) - return{ 0, static_cast(primary) }; + return{ 0, static_cast(row.first) }; //First of all, find the text of secondary. - auto real_str = mtr.line_sections[secondary]; + auto real_str = mtr.line_sections[row.second]; - std::unique_ptr mask_str; + auto text_ptr = real_str.begin; + const auto text_size = real_str.end - real_str.begin; + + std::wstring mask_str; if (editor_.mask_char_) - mask_str.reset(new std::wstring(real_str.end - real_str.begin, editor_.mask_char_)); - - const wchar_t * str = (editor_.mask_char_ ? mask_str->data() : real_str.begin); + { + mask_str.resize(text_size, editor_.mask_char_); + text_ptr = mask_str.c_str(); + } std::vector reordered; - unicode_bidi bidi; - bidi.linestr(str, real_str.end - real_str.begin, reordered); + unicode_bidi{}.linestr(text_ptr, text_size, reordered); - nana::upoint res(static_cast(real_str.begin - mtr.line_sections.front().begin), static_cast(primary)); + nana::upoint res(static_cast(real_str.begin - mtr.line_sections.front().begin), static_cast(row.first)); scrpos.x -= editor_.text_area_.area.x; if (scrpos.x < 0) scrpos.x = 0; for (auto & ent : reordered) { - std::size_t len = ent.end - ent.begin; - auto str_px = static_cast(editor_._m_text_extent_size(ent.begin, len).width); + auto str_px = static_cast(editor_._m_text_extent_size(ent.begin, ent.end - ent.begin).width); if (scrpos.x < str_px) { - std::unique_ptr pxbuf(new unsigned[len]); - - res.x += editor_._m_char_by_pixels(ent.begin, len, pxbuf.get(), str_px, scrpos.x, is_right_text(ent)); - res.x += static_cast(ent.begin - str); + res.x += editor_._m_char_by_pixels(ent, scrpos.x); + res.x += static_cast(ent.begin - text_ptr); return res; } scrpos.x -= str_px; @@ -888,11 +885,10 @@ namespace nana{ namespace widgets if (0 == scrlines) return false; - auto & points = editor_.points_; + const auto & points = editor_.points_; editor_._m_get_scrollbar_size(); - std::size_t off_secondary; - auto off_primary = _m_textline(points.offset.y, off_secondary); + auto off_coord = _m_textline(points.offset.y); unsigned caret_secondary; { @@ -902,26 +898,26 @@ namespace nana{ namespace widgets } //Use the caret line for the offset line when caret is in front of current offset line. - if (off_primary > points.caret.y || ((off_primary == points.caret.y) && (off_secondary > caret_secondary))) + if (off_coord.first > points.caret.y || ((off_coord.first == points.caret.y) && (off_coord.second > caret_secondary))) { //Use the line which was specified by points.caret for the first line. - _m_set_offset_by_secondary(points.caret.y, caret_secondary); + _m_set_offset_by_secondary(row_coordinate(points.caret.y, caret_secondary)); return true; } //Find the last screen line. If the text does not reach the bottom of screen, //do not adjust the offset line. - nana::upoint bottom; //x=primary, y=secondary - if (false == _m_advance_secondary(off_primary, off_secondary, static_cast(scrlines - 1), bottom)) + row_coordinate bottom; + if (false == _m_advance_secondary(off_coord, static_cast(scrlines - 1), bottom)) return false; //Do not adjust the offset line if the caret line does not reach the bottom line. - if (points.caret.y < bottom.x || ((points.caret.y == bottom.x) && (caret_secondary <= bottom.y))) + if (points.caret.y < bottom.first || ((points.caret.y == bottom.first) && (caret_secondary <= bottom.second))) return false; - _m_advance_secondary(points.caret.y, caret_secondary, -static_cast(scrlines - 1), bottom); + _m_advance_secondary(row_coordinate(points.caret.y, caret_secondary), -static_cast(scrlines - 1), bottom); - _m_set_offset_by_secondary(bottom.x, bottom.y); + _m_set_offset_by_secondary(bottom); return true; } private: @@ -929,13 +925,13 @@ namespace nana{ namespace widgets { if (str.empty()) { - tsec.emplace_back(str.data(), str.data()); + tsec.emplace_back(str.c_str(), str.c_str(), unsigned{}); return; } - const auto end = str.data() + str.size(); + const auto end = str.c_str() + str.size(); const wchar_t * word = nullptr; - for (auto i = str.data(); i != end; ++i) + for (auto i = str.c_str(); i != end; ++i) { wchar_t const ch = *i; @@ -944,11 +940,11 @@ namespace nana{ namespace widgets { if (word) //Record the word. { - tsec.emplace_back(word, i); + tsec.emplace_back(word, i, unsigned{}); word = nullptr; } - tsec.emplace_back(i, i + 1); + tsec.emplace_back(i, i + 1, unsigned{}); continue; } @@ -957,26 +953,25 @@ namespace nana{ namespace widgets } if(word) - tsec.emplace_back(word, end); + tsec.emplace_back(word, end, unsigned{}); } - void _m_set_offset_by_secondary(std::size_t primary, std::size_t secondary) + void _m_set_offset_by_secondary(row_coordinate row) { - for (auto i = linemtr_.begin(), end = linemtr_.begin() + primary; i != end; ++i) - secondary += i->take_lines; + for (auto i = linemtr_.begin(), end = linemtr_.begin() + row.first; i != end; ++i) + row.second += i->take_lines; - editor_.points_.offset.y = static_cast(secondary); + editor_.points_.offset.y = static_cast(row.second); } - bool _m_advance_secondary(std::size_t primary, std::size_t secondary, int distance, nana::upoint& new_sec) + bool _m_advance_secondary(row_coordinate row, int distance, row_coordinate& new_row) { - if ((primary >= linemtr_.size()) || (secondary >= linemtr_[primary].take_lines)) + if ((row.first >= linemtr_.size()) || (row.second >= linemtr_[row.first].take_lines)) return false; if (0 == distance) { - new_sec.x = static_cast(primary); - new_sec.y = static_cast(secondary); + new_row = row; return true; } @@ -984,32 +979,32 @@ namespace nana{ namespace widgets { std::size_t n = static_cast(-distance); - if (secondary > n) + if (row.second > n) { - new_sec.x = static_cast(primary); - new_sec.y = static_cast(secondary - n); + new_row.first = row.first; + new_row.second = row.second - n; return true; } - if (0 == primary) + if (0 == row.first) return false; - --primary; - n -= (secondary + 1); + --row.first; + n -= (row.second + 1); while (true) { - auto lines = linemtr_[primary].take_lines; + auto lines = linemtr_[row.first].take_lines; if (lines >= n) { - new_sec.x = static_cast(primary); - new_sec.y = static_cast(lines - n); + new_row.first = row.first; + new_row.second = lines - n; return true; } - if (0 == primary) + if (0 == row.first) return false; - --primary; + --row.first; n -= lines; } } @@ -1017,22 +1012,24 @@ namespace nana{ namespace widgets { std::size_t n = static_cast(distance); - if (linemtr_[primary].take_lines - (secondary + 1) >= n) + auto delta_lines = linemtr_[row.first].take_lines - (row.second + 1); + + if (delta_lines >= n) { - new_sec.x = static_cast(primary); - new_sec.y = static_cast(secondary + n); + new_row.first = row.first; + new_row.second = row.second + n; return true; } - n -= (linemtr_[primary].take_lines - (secondary + 1)); + n -= delta_lines; - while (++primary < linemtr_.size()) + while (++row.first < linemtr_.size()) { - auto & mtr = linemtr_[primary]; + auto & mtr = linemtr_[row.first]; if (mtr.take_lines >= n) { - new_sec.x = static_cast(primary); - new_sec.y = static_cast(n - 1); + new_row.first = row.first; + new_row.second = n - 1; return true; } n -= mtr.take_lines; @@ -1051,10 +1048,9 @@ namespace nana{ namespace widgets return false; auto & section = mtr.line_sections[secondary.y]; - unsigned len = static_cast(section.end - section.begin); - auto chptr = section.begin + (secondary.x > len ? len : secondary.x); - pos = static_cast(chptr - editor_.textbase_.getline(textline).data()); + auto chptr = section.begin + (std::min)(secondary.x, static_cast(section.end - section.begin)); + pos = static_cast(chptr - editor_.textbase_.getline(textline).c_str()); return true; } @@ -1082,57 +1078,49 @@ namespace nana{ namespace widgets } - std::size_t _m_textline(std::size_t scrline, std::size_t & secondary) const + row_coordinate _m_textline(std::size_t scrline) const { - secondary = 0; - std::size_t primary = 0; - + row_coordinate coord; for (auto & mtr : linemtr_) { if (mtr.take_lines > scrline) { - secondary = scrline; - return primary; + coord.second = scrline; + return coord; } else scrline -= mtr.take_lines; - ++primary; + ++coord.first; } - - return primary; + return coord; } //secondary, index of line that the text was splitted into multilines. - std::size_t _m_textline_from_screen(int y, std::size_t & secondary) const + row_coordinate _m_textline_from_screen(int y) const { + row_coordinate coord; + const auto line_px = static_cast(editor_.line_height()); + + if ((0 == editor_.textbase_.lines()) || (0 == line_px)) + return coord; + const int text_area_top = editor_.text_area_.area.y; const int offset_top = editor_.points_.offset.y; - if (0 == editor_.textbase_.lines()) - { - secondary = 0; - return 0; - } - - std::size_t screen_line; - if (y < text_area_top) - { - screen_line = (text_area_top - y) / static_cast(editor_.line_height()); - if (screen_line > static_cast(offset_top)) - screen_line = 0; - else - screen_line = static_cast(offset_top)-screen_line; - } + auto screen_line = (text_area_top - y) / line_px; + if ((y < text_area_top) && (screen_line > offset_top)) + screen_line = 0; else - screen_line = static_cast((y - text_area_top) / static_cast(editor_.line_height()) + offset_top); + screen_line = offset_top - screen_line; - auto primary = _m_textline(screen_line, secondary); - if (primary < linemtr_.size()) - return primary; - - secondary = linemtr_.back().line_sections.size() - 1; - return linemtr_.size() - 1; + coord = _m_textline(static_cast(screen_line)); + if (linemtr_.size() <= coord.first) + { + coord.first = linemtr_.size() - 1; + coord.second = linemtr_.back().line_sections.size() - 1; + } + return coord; } private: text_editor& editor_; @@ -1183,25 +1171,6 @@ namespace nana{ namespace widgets std::vector entities; - auto test_whole_word = [&text](index pos, index len) - { - if (pos) - { - auto chr = text[pos - 1]; - if ((std::iswalpha(chr) && !std::isspace(chr)) || chr == '_') - return false; - } - - if (pos + len < text.size()) - { - auto chr = text[pos + len]; - if ((std::iswalpha(chr) && !std::isspace(chr)) || chr == '_') - return false; - } - - return true; - }; - ::nana::ciwstring cistr; for (auto & ds : kwptr->kwbase) { @@ -1216,22 +1185,22 @@ namespace nana{ namespace widgets if (ds.whole_word_matched) { - if (!test_whole_word(pos, ds.text.size())) + if (!_m_whole_word(text, pos, ds.text.size())) continue; } } else { if (cistr.empty()) - cistr.append(text.data(), text.size()); + cistr.append(text.c_str(), text.size()); - pos = cistr.find(ds.text.data(), pos); + pos = cistr.find(ds.text.c_str(), pos); if (pos == cistr.npos) break; if (ds.whole_word_matched) { - if (!test_whole_word(pos, ds.text.size())) + if (!_m_whole_word(text, pos, ds.text.size())) continue; } } @@ -1241,7 +1210,7 @@ namespace nana{ namespace widgets { entities.emplace_back(); auto & last = entities.back(); - last.begin = text.data() + pos; + last.begin = text.c_str() + pos; last.end = last.begin + ds.text.size(); last.scheme = ki->second.get(); } @@ -1274,6 +1243,25 @@ namespace nana{ namespace widgets { 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::isspace(chr)) || chr == '_') + return false; + } + + if (pos + len < text.size()) + { + auto chr = text[pos + len]; + if ((std::iswalpha(chr) && !std::isspace(chr)) || chr == '_') + return false; + } + + return true; + } private: std::vector entities_; }; @@ -1281,18 +1269,23 @@ namespace nana{ namespace widgets //class text_editor text_editor::text_editor(window wd, graph_reference graph, const text_editor_scheme* schm) : behavior_(new behavior_normal(*this)), - window_(wd), graph_(graph), + window_(wd), + caret_(API::open_caret(wd, true)), + graph_(graph), scheme_(schm), keywords_(new keywords) { - text_area_.area = graph.size(); + text_area_.area.dimension(graph.size()); text_area_.captured = false; text_area_.tab_space = 4; text_area_.scroll_pixels = 16; text_area_.hscroll = text_area_.vscroll = 0; - select_.mode_selection = selection::mode_no_selected; - select_.dragged = false; - API::create_caret(wd, 1, line_height()); + select_.behavior = text_focus_behavior::select_if_tabstop_or_click; + select_.move_to_end = false; + select_.mode_selection = selection::mode::no_selected; + select_.ignore_press = false; + + API::create_caret(wd, { 1, line_height() }); API::bgcolor(wd, colors::white); API::fgcolor(wd, colors::black); @@ -1313,7 +1306,7 @@ namespace nana{ namespace widgets void text_editor::set_highlight(const std::string& name, const ::nana::color& fgcolor, const ::nana::color& bgcolor) { - if (fgcolor.invisible() && fgcolor.invisible()) + if (fgcolor.invisible() && bgcolor.invisible()) { keywords_->schemes.erase(name); return; @@ -1342,17 +1335,20 @@ namespace nana{ namespace widgets return; } } + keywords_->kwbase.emplace_back(kw, name, case_sensitive, whole_word_matched); } void text_editor::erase_keyword(const ::std::wstring& kw) { for (auto i = keywords_->kwbase.begin(); i != keywords_->kwbase.end(); ++i) + { if (i->text == kw) { keywords_->kwbase.erase(i); return; } + } } void text_editor::set_accept(std::function pred) @@ -1445,7 +1441,7 @@ namespace nana{ namespace widgets void text_editor::indent(bool enb, std::function generator) { indent_.enabled = enb; - indent_.generator.swap(generator); + indent_.generator = std::move(generator); } void text_editor::set_event(event_interface* ptr) @@ -1520,13 +1516,12 @@ namespace nana{ namespace widgets rectangle text_editor::text_area(bool including_scroll) const { - if (including_scroll) - return text_area_.area; - auto r = text_area_.area; - - r.width = text_area_.area.width > text_area_.vscroll ? text_area_.area.width - text_area_.vscroll : 0; - r.height = text_area_.area.height > text_area_.hscroll ? text_area_.area.height - text_area_.hscroll : 0; + if (!including_scroll) + { + r.width = r.width > text_area_.vscroll ? r.width - text_area_.vscroll : 0; + r.height = r.height > text_area_.hscroll ? r.height - text_area_.hscroll : 0; + } return r; } @@ -1624,35 +1619,72 @@ namespace nana{ namespace widgets return 0; } - bool text_editor::mouse_enter(bool enter) + bool text_editor::focus_changed(const arg_focus& arg) { - if((false == enter) && (false == text_area_.captured)) + 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(); + 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); - if(API::focus_window() == window_) - return false; - - render(false); - return true; + 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)) + if(((!hit_text_area(scrpos)) && (!text_area_.captured)) || !attributes_.editable || !API::window_enabled(window_)) cur = cursor::arrow; API::window_cursor(window_, cur); + if(!attributes_.editable) + return false; + if(left_button) { - auto caret_pos_before = caret(); mouse_caret(scrpos); - if(select_.mode_selection != selection::mode_no_selected) + if (selection::mode::mouse_selected == select_.mode_selection || selection::mode::method_selected == select_.mode_selection) set_end_caret(); - else if ((!select_.dragged) && (caret_pos_before != caret())) - select_.dragged = true; text_area_.border_renderer(graph_, _m_bgcolor()); return true; @@ -1662,36 +1694,50 @@ namespace nana{ namespace widgets bool text_editor::mouse_pressed(const arg_mouse& arg) { + if(!attributes_.editable) + return false; + if (event_code::mouse_down == arg.evt_code) { - if (!hit_text_area(arg.pos)) + if (select_.ignore_press || (!hit_text_area(arg.pos))) + { + select_.ignore_press = false; return false; + } if (::nana::mouse::left_button == arg.button) { - API::capture_window(window_, true); + API::set_capture(window_, true); text_area_.captured = true; - //Set caret pos by screen point and get the caret pos. - mouse_caret(arg.pos); - if (arg.shift) + + if (this->hit_select_area(behavior_->screen_to_caret(arg.pos), true)) { - if (points_.shift_begin_caret != points_.caret) - { - select_.a = points_.shift_begin_caret; - select_.b = points_.caret; - } + 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); + if (arg.shift) { - select_.a = points_.caret; //Set begin caret - set_end_caret(); + 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(); + } + points_.shift_begin_caret = points_.caret; + } + select_.mode_selection = selection::mode::mouse_selected; } - select_.mode_selection = selection::mode_mouse_selected; } text_area_.border_renderer(graph_, _m_bgcolor()); @@ -1699,29 +1745,30 @@ namespace nana{ namespace widgets } else if (event_code::mouse_up == arg.evt_code) { - auto is_prev_no_selected = (select_.mode_selection == selection::mode_no_selected); + select_.ignore_press = false; + bool updated = false; - if (select_.mode_selection == selection::mode_mouse_selected) + if (select_.mode_selection == selection::mode::mouse_selected) { - select_.mode_selection = selection::mode_no_selected; + select_.mode_selection = selection::mode::no_selected; set_end_caret(); } - else if (is_prev_no_selected) + else if (selection::mode::move_selected == select_.mode_selection) { - if ((!select_.dragged) || (!move_select())) + if (!move_select()) select(false); + updated = true; } - select_.dragged = false; - API::capture_window(window_, false); + API::release_capture(window_); + text_area_.captured = false; if (hit_text_area(arg.pos) == false) API::window_cursor(window_, nana::cursor::arrow); text_area_.border_renderer(graph_, _m_bgcolor()); - //Redraw if is_prev_no_selected is true - return is_prev_no_selected; + return updated; } return false; } @@ -1745,12 +1792,34 @@ namespace nana{ namespace widgets return true; } - void text_editor::text(std::wstring str) + void text_editor::text(std::wstring str, bool end_caret) { + undo_.clear(); + textbase_.erase_all(); _m_reset(); behavior_->pre_calc_lines(width_pixels()); - put(std::move(str)); + 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)); + + undo_.push(std::move(undo_ptr)); + + if (graph_) + { + behavior_->adjust_caret_into_screen(); + reset_caret(); + render(API::is_focus_ready(window_)); + _m_scrollbar(); + + points_.xpos = 0; + } + } + else + put(std::move(str)); } std::wstring text_editor::text() const @@ -1785,15 +1854,17 @@ namespace nana{ namespace widgets { visible = true; if (line_bottom > _m_end_pos(false)) - API::caret_size(window_, nana::size(1, line_pixels - (line_bottom - _m_end_pos(false)))); - else if (API::caret_size(window_).height != line_pixels) + caret_->dimension(nana::size(1, line_pixels - (line_bottom - _m_end_pos(false)))); + else if (caret_->dimension().height != line_pixels) reset_caret_pixels(); } - API::caret_visible(window_, visible); + if(!attributes_.editable) + visible = false; + caret_->visible(visible); if(visible) - API::caret_pos(window_, pos); + caret_->position(pos); } void text_editor::move_caret_end() @@ -1805,7 +1876,7 @@ namespace nana{ namespace widgets void text_editor::reset_caret_pixels() const { - API::caret_size(window_, nana::size(1, line_height())); + caret_->dimension({ 1, line_height() }); } void text_editor::reset_caret() @@ -1816,7 +1887,7 @@ namespace nana{ namespace widgets void text_editor::show_caret(bool isshow) { if(isshow == false || API::is_focus_ready(window_)) - API::caret_visible(window_, isshow); + caret_->visible(isshow); } bool text_editor::selected() const @@ -1842,12 +1913,12 @@ namespace nana{ namespace widgets select_.b.y = static_cast(textbase_.lines()); if(select_.b.y) --select_.b.y; select_.b.x = static_cast(textbase_.getline(select_.b.y).size()); - select_.mode_selection = selection::mode_method_selected; + select_.mode_selection = selection::mode::method_selected; render(true); return true; } - select_.mode_selection = selection::mode_no_selected; + select_.mode_selection = selection::mode::no_selected; if (_m_cancel_select(0)) { render(true); @@ -1861,11 +1932,21 @@ namespace nana{ namespace widgets return ((text_area_.area.x <= pos.x && pos.x < _m_end_pos(true)) && (text_area_.area.y <= pos.y && pos.y < _m_end_pos(false))); } - bool text_editor::hit_select_area(nana::upoint pos) const + bool text_editor::hit_select_area(nana::upoint pos, bool ignore_when_select_all) const { nana::upoint a, b; if(_m_get_sort_select_points(a, b)) { + if (ignore_when_select_all) + { + 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; } @@ -1874,7 +1955,10 @@ namespace nana{ namespace widgets bool text_editor::move_select() { - if(hit_select_area(points_.caret) || (select_.b == points_.caret)) + if (! attributes_.editable) + return false; + + if(hit_select_area(points_.caret, true) || (select_.b == points_.caret)) { points_.caret = select_.b; @@ -1909,7 +1993,7 @@ namespace nana{ namespace widgets if (attributes_.line_wrapped) exclude_px = text_area_.vscroll; else - exclude_px = API::caret_size(window_).width; + exclude_px = caret_->dimension().width; return (text_area_.area.width > exclude_px ? text_area_.area.width - exclude_px : 0); } @@ -1924,11 +2008,21 @@ namespace nana{ namespace widgets return 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; + } + void text_editor::draw_corner() { if(text_area_.vscroll && text_area_.hscroll) { - graph_.rectangle({ text_area_.area.right() - static_cast(text_area_.vscroll), text_area_.area.bottom() - static_cast(text_area_.hscroll), text_area_.vscroll, text_area_.hscroll }, + graph_.rectangle(rectangle{ text_area_.area.right() - static_cast(text_area_.vscroll), text_area_.area.bottom() - static_cast(text_area_.hscroll), text_area_.vscroll, text_area_.hscroll }, true, colors::button_face); } } @@ -1952,17 +2046,16 @@ namespace nana{ namespace widgets ext_renderer_.background(graph_, text_area_.area, bgcolor); if(attributes_.counterpart && !text_area_.area.empty()) - attributes_.counterpart.bitblt(nana::rectangle(0, 0, text_area_.area.width, text_area_.area.height), graph_, nana::point(text_area_.area.x, text_area_.area.y)); + attributes_.counterpart.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 = behavior_->render(fgcolor); + auto text_pos = behavior_->render(fgcolor); - if (text_pos.empty()) - text_pos.push_back({ 0, 0 }); + text_pos.push_back(upoint{}); if (text_pos != text_position_) { @@ -1975,7 +2068,7 @@ namespace nana{ namespace widgets graph_.string({ text_area_.area.x - points_.offset.x, text_area_.area.y }, attributes_.tip_string, static_cast(0x787878)); if (text_position_.empty()) - text_position_.push_back({ 0, 0 }); + text_position_.push_back(upoint{}); draw_corner(); @@ -1984,6 +2077,9 @@ namespace nana{ namespace widgets //public: void text_editor::put(std::wstring text) { + if (text.empty()) + return; + auto undo_ptr = std::unique_ptr{ new undo_input_text(*this, text) }; undo_ptr->set_selected_text(); @@ -2024,7 +2120,8 @@ namespace nana{ namespace widgets auto secondary_before = behavior_->take_lines(points_.caret.y); textbase_.insert(points_.caret, std::move(ch_str)); - behavior_->pre_calc_line(points_.caret.y, width_pixels()); + _m_pre_calc_lines(points_.caret.y, 1); + points_.caret.x ++; if (refresh || _m_update_caret_line(secondary_before)) @@ -2039,9 +2136,9 @@ namespace nana{ namespace widgets void text_editor::copy() const { - std::wstring str; - if(_m_make_select_string(str)) - nana::system::dataexch().set(str); + auto text = _m_make_select_string(); + if (!text.empty()) + nana::system::dataexch().set(text); } void text_editor::cut() @@ -2105,10 +2202,8 @@ namespace nana{ namespace widgets if (record_undo) undo_.push(std::move(undo_ptr)); - const auto width_px = width_pixels(); behavior_->add_lines(points_.caret.y - 1, 1); - behavior_->pre_calc_line(points_.caret.y, width_px); - behavior_->pre_calc_line(points_.caret.y - 1, width_px); + _m_pre_calc_lines(points_.caret.y - 1, 2); points_.caret.x = 0; @@ -2120,27 +2215,21 @@ namespace nana{ namespace widgets if (indent_.enabled) { - std::wstring indent_text; if (indent_.generator) { - indent_text = to_wstring(indent_.generator()); + put(to_wstring(indent_.generator())); } 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) - indent_text = text.substr(0, indent_pos); + put(text.substr(0, indent_pos)); else - indent_text = text; + put(text); } - - if (indent_text.size()) - put(indent_text); - } - if (behavior_->adjust_caret_into_screen() || need_refresh) render(true); @@ -2189,7 +2278,8 @@ namespace nana{ namespace widgets undo_ptr->set_removed(lnstr.substr(points_.caret.x, erase_number)); auto secondary = behavior_->take_lines(points_.caret.y); textbase_.erase(points_.caret.y, points_.caret.x, erase_number); - behavior_->pre_calc_line(points_.caret.y, width_pixels()); + _m_pre_calc_lines(points_.caret.y, 1); + if(_m_move_offset_x_while_over_border(-2) == false) { behavior_->update_line(points_.caret.y, secondary); @@ -2238,7 +2328,6 @@ namespace nana{ namespace widgets behavior_->adjust_caret_into_screen(); render(true); _m_scrollbar(); - } void text_editor::move_ns(bool to_north) @@ -2339,27 +2428,45 @@ namespace nana{ namespace widgets size_t lnsz = textbase_.getline(caret.y).size(); switch (key) { case keyboard::os_arrow_left: - if (caret.x != 0) { - --caret.x; + if (select_.move_to_end && (select_.a != select_.b) && (!arg.shift)) + { + caret = select_.a; changed = true; - }else { - if (caret.y != 0) { - --caret.y; - caret.x = static_cast(textbase_.getline(caret.y).size()); + } + else + { + if (caret.x != 0) { + --caret.x; changed = true; } + else { + if (caret.y != 0) { + --caret.y; + caret.x = static_cast(textbase_.getline(caret.y).size()); + changed = true; + } + } } break; case keyboard::os_arrow_right: - if (caret.x < lnsz) { - ++caret.x; + if (select_.move_to_end && (select_.a != select_.b) && (!arg.shift)) + { + caret = select_.b; changed = true; - }else { - if (caret.y != nlines - 1) { - ++caret.y; - caret.x = 0; + } + else + { + if (caret.x < lnsz) { + ++caret.x; changed = true; } + else { + if (caret.y != nlines - 1) { + ++caret.y; + caret.x = 0; + changed = true; + } + } } break; case keyboard::os_arrow_up: @@ -2411,6 +2518,7 @@ namespace nana{ namespace widgets if (select_.a != caret || select_.b != caret) { changed = true; } + if (changed) { if (arg.shift) { switch (key) { @@ -2464,7 +2572,7 @@ namespace nana{ namespace widgets { if(vert && attributes_.vscroll) { - attributes_.vscroll->make_step(!upwards); + attributes_.vscroll->make_step(!upwards, this->scheme_->mouse_wheel.lines); if(_m_scroll_text(true)) { render(true); @@ -2474,6 +2582,13 @@ namespace nana{ namespace widgets return false; } + 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->behavior_->pre_calc_line(pos, width_px); + } + bool text_editor::_m_accepts(char_type ch) const { if (accepts::no_restrict == attributes_.acceptive) @@ -2522,27 +2637,23 @@ namespace nana{ namespace widgets return false; } - void text_editor::_m_on_scroll(const arg_mouse& arg) - { - if((arg.evt_code == event_code::mouse_move) && (arg.left_button == false)) - return; - - bool vert = (attributes_.vscroll && (attributes_.vscroll->handle() == arg.window_handle)); - if(_m_scroll_text(vert)) - { - render(true); - reset_caret(); - API::update_window(window_); - } - } - void text_editor::_m_scrollbar() { _m_get_scrollbar_size(); nana::size tx_area = _m_text_area(); - auto scroll_fn = [this](const arg_mouse& arg) { - _m_on_scroll(arg); + auto scroll_fn = [this](const arg_mouse& arg) + { + if ((arg.evt_code == event_code::mouse_move) && (arg.left_button == false)) + return; + + bool vert = (attributes_.vscroll && (attributes_.vscroll->handle() == arg.window_handle)); + if (_m_scroll_text(vert)) + { + render(true); + reset_caret(); + API::update_window(window_); + } }; if (text_area_.vscroll) @@ -2685,11 +2796,8 @@ namespace nana{ namespace widgets textbase_.insertln(++crtpos.y, text.substr(backpos.first, backpos.second - backpos.first) + str_orig.substr(x_orig)); crtpos.x = static_cast(backpos.second - backpos.first); - const auto width_px = width_pixels(); behavior_->add_lines(points_.caret.y, lines.size() - 1); - const auto endline = points_.caret.y + lines.size(); - for (auto i = points_.caret.y; i < endline; ++i) - behavior_->pre_calc_line(i, width_px); + _m_pre_calc_lines(points_.caret.y, lines.size()); } else { @@ -2701,7 +2809,7 @@ namespace nana{ namespace widgets textbase_.insert(crtpos, std::move(text)); crtpos.x += static_cast(length); - behavior_->pre_calc_line(crtpos.y, width_pixels()); + _m_pre_calc_lines(crtpos.y, 1); } return crtpos; } @@ -2724,7 +2832,7 @@ namespace nana{ namespace widgets else { textbase_.erase(a.y, a.x, b.x - a.x); - behavior_->pre_calc_line(a.y, width_pixels()); + _m_pre_calc_lines(a.y, 1); } select_.a = select_.b; @@ -2734,27 +2842,29 @@ namespace nana{ namespace widgets return points_.caret; } - bool text_editor::_m_make_select_string(std::wstring& text) const + std::wstring text_editor::_m_make_select_string() const { + std::wstring text; + nana::upoint a, b; - if (!_m_get_sort_select_points(a, b)) - return false; - - if(a.y != b.y) + if (_m_get_sort_select_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) + if (a.y != b.y) { - text += textbase_.getline(i); + 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); } - text += textbase_.getline(b.y).substr(0, b.x); + else + text = textbase_.getline(a.y).substr(a.x, b.x - a.x); } - else - text = textbase_.getline(a.y).substr(a.x, b.x - a.x); - return true; + return text; } std::size_t eat_endl(const wchar_t* str, std::size_t pos) @@ -2784,7 +2894,7 @@ namespace nana{ namespace widgets bool text_editor::_m_resolve_text(const std::wstring& text, std::vector> & lines) { - auto const text_str = text.data(); + auto const text_str = text.c_str(); std::size_t begin = 0; while (true) { @@ -2810,14 +2920,14 @@ namespace nana{ namespace widgets auto eats = eat_endl(chp, 0); if (eats) { - lines.emplace_back(0, 0); + lines.emplace_back(); chp += (eats - 1); } } if (text.npos == begin) { - lines.emplace_back(0, 0); + lines.emplace_back(); break; } } @@ -2858,9 +2968,6 @@ namespace nana{ namespace widgets nana::size text_editor::_m_text_extent_size(const char_type* str, size_type n) const { - if (!graph_) - return{}; - if(mask_char_) { std::wstring maskstr; @@ -2910,19 +3017,22 @@ namespace nana{ namespace widgets bool text_editor::_m_move_select(bool record_undo) { nana::upoint caret = points_.caret; - std::wstring text; - if (_m_make_select_string(text)) + 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; _m_get_sort_select_points(a, b); if (caret.y < a.y || (caret.y == a.y && caret.x < a.x)) {//forward - _m_erase_select(); - undo_ptr->set_caret_pos(); + + _m_erase_select(); _m_put(text); select_.a = caret; @@ -2931,6 +3041,7 @@ namespace nana{ namespace widgets else if (b.y < caret.y || (caret.y == b.y && b.x < caret.x)) { undo_ptr->set_caret_pos(); + _m_put(text); _m_erase_select(); @@ -2940,13 +3051,17 @@ namespace nana{ namespace widgets } 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); undo_.push(std::move(undo_ptr)); } - points_.caret = select_.a; + points_.caret = select_.b; reset_caret(); return true; } @@ -3003,7 +3118,7 @@ namespace nana{ namespace widgets if (str <= ent.begin && ent.begin < str_end) { ent_begin = ent.begin; - ent_off = std::accumulate(glyphs, glyphs + (ent.begin - str), 0); + ent_off = static_cast(std::accumulate(glyphs, glyphs + (ent.begin - str), unsigned{})); } else if (ent.begin <= str && str < ent.end) ent_begin = str; @@ -3019,268 +3134,243 @@ namespace nana{ namespace widgets canvas.rectangle(true); ent_pos.x += ent_off; + + if (rtl) { //draw the whole text if it is a RTL text, because Arbic language is transformable. canvas.string({}, str, len); - graph_.bitblt(::nana::rectangle{ ent_pos, ::nana::size{ ent_pixels, canvas.height() } }, canvas, ::nana::point{ ent_off, 0 }); } else { canvas.string({}, ent_begin, ent_end - ent_begin); - graph_.bitblt(::nana::rectangle{ ent_pos, ::nana::size{ ent_pixels, canvas.height() } }, canvas); + ent_off = 0; } + graph_.bitblt(rectangle{ ent_pos, size{ ent_pixels, canvas.height() } }, canvas, point{ ent_off, 0 }); } } } - void text_editor::_m_draw_string(int top, const ::nana::color& clr, const nana::upoint& str_pos, const std::wstring& str, bool if_mask) const + class text_editor::helper_pencil { - ::nana::point text_pos{ text_area_.area.x - points_.offset.x, top }; + public: + helper_pencil(paint::graphics& graph, const text_editor& editor, const color_proxy& selection_fgcolor, const color_proxy& selection_bgcolor, keyword_parser& parser): + graph_( graph ), + editor_( editor ), + parser_( parser ), + selection_fgcolor_( selection_fgcolor ), + selection_bgcolor_( selection_bgcolor ), + line_px_( editor.line_height() ) + {} + + void write_selection(const point& text_pos, unsigned text_px, const wchar_t* text, std::size_t len) + { + graph_.palette(true, selection_fgcolor_.get_color()); + graph_.rectangle(::nana::rectangle{ text_pos, { text_px, line_px_ } }, true, selection_bgcolor_); + graph_.string(text_pos, text, len); + } + + void rtl_string(point strpos, const wchar_t* str, std::size_t len, std::size_t str_px, unsigned glyph_front, unsigned glyph_selected) + { + editor_._m_draw_parse_string(parser_, true, strpos, selection_fgcolor_.get_color(), str, len); + + //Draw selected part + paint::graphics graph({ glyph_selected, line_px_ }); + graph.typeface(this->graph_.typeface()); + graph.rectangle(true, selection_bgcolor_.get_color()); + + int sel_xpos = static_cast(str_px - (glyph_front + glyph_selected)); + + graph.palette(true, selection_fgcolor_.get_color()); + graph.string({ -sel_xpos, 0 }, str, len); + 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_; + const color_proxy & selection_fgcolor_; + const color_proxy & selection_bgcolor_; + unsigned line_px_; + }; + + void text_editor::_m_draw_string(int top, const ::nana::color& clr, const nana::upoint& text_coord, const std::wstring& text, bool if_mask) const + { + + point text_draw_pos{ text_area_.area.x - points_.offset.x, top }; const int text_right = text_area_.area.right(); - std::unique_ptr mask_str; + auto text_ptr = &text; + + std::wstring mask_str; if (if_mask && mask_char_) - mask_str.reset(new std::wstring(str.size(), mask_char_)); + { + mask_str.resize(text.size(), mask_char_); + text_ptr = &mask_str; + } - bool focused = API::is_focus_ready(window_); + const auto focused = API::is_focus_ready(window_); - auto & linestr = (if_mask && mask_char_ ? *mask_str : str); - - unicode_bidi bidi; std::vector reordered; - bidi.linestr(linestr.c_str(), linestr.size(), reordered); + unicode_bidi{}.linestr(text_ptr->c_str(), text_ptr->size(), reordered); //Parse highlight keywords keyword_parser parser; - parser.parse(linestr, keywords_.get()); - - auto whitespace_w = graph_.text_extent_size(L" ", 1).width; + parser.parse(*text_ptr, keywords_.get()); const auto line_h_pixels = line_height(); - //The line of text is in the range of selection - nana::upoint a, b; + helper_pencil pencil(graph_, *this, scheme_->selection_text, scheme_->selection, parser); + + graph_.palette(true, clr); graph_.palette(false, scheme_->selection.get_color()); - //The text is not selected or the whole line text is selected - if (!focused || (!_m_get_sort_select_points(a, b)) || (select_.a.y != str_pos.y && select_.b.y != str_pos.y)) + + //Get the selection begin and end position of the current text. + const wchar_t *sbegin = nullptr, *send = nullptr; + + nana::upoint a, b; + if (_m_get_sort_select_points(a, b)) + { + if (a.y < text_coord.y && text_coord.y < b.y) + { + sbegin = text_ptr->c_str(); + send = sbegin + text_ptr->size(); + } + 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 + static_cast(text_ptr->size()), b.x); + + if (sbegin_pos < send_pos) + { + sbegin = text_ptr->c_str() + (sbegin_pos - text_coord.x); + send = text_ptr->c_str() + (send_pos - text_coord.x); + } + } + else if (a.y == text_coord.y) + { + if (a.x < text_coord.x + text_ptr->size()) + { + sbegin = text_ptr->c_str(); + if (text_coord.x < a.x) + sbegin += (a.x - text_coord.x); + + send = text_ptr->c_str() + text_ptr->size(); + } + } + else if (b.y == text_coord.y) + { + if (text_coord.x < b.x) + { + sbegin = text_ptr->c_str(); + send = text_ptr->c_str() + (std::min)(b.x - text_coord.x, static_cast(text_ptr->size())); + } + } + } + + //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; + + const bool text_selected = (sbegin == text_ptr->c_str() && send == text_ptr->c_str() + text_ptr->size()); + //The text is not selected or the whole line text is selected + if (!focused || (!sbegin || !send) || text_selected || !attributes_.editable) { - bool selected = (a.y < str_pos.y && str_pos.y < b.y); for (auto & ent : reordered) { std::size_t len = ent.end - ent.begin; unsigned str_w = graph_.text_extent_size(ent.begin, len).width; - if ((text_pos.x + static_cast(str_w) > text_area_.area.x) && (text_pos.x < text_right)) + if ((text_draw_pos.x + static_cast(str_w) > text_area_.area.x) && (text_draw_pos.x < text_right)) { - if (selected && focused) - { - graph_.palette(true, scheme_->selection_text.get_color()); - graph_.rectangle(::nana::rectangle{ text_pos, { str_w, line_h_pixels } }, true); - graph_.string(text_pos, ent.begin, len); - } + if (text_selected && focused) + pencil.write_selection(text_draw_pos, str_w, ent.begin, len); else - _m_draw_parse_string(parser, is_right_text(ent), text_pos, clr, ent.begin, len); + _m_draw_parse_string(parser, is_right_text(ent), text_draw_pos, clr, ent.begin, len); } - text_pos.x += static_cast(str_w); + text_draw_pos.x += static_cast(str_w); } - if (selected) - graph_.rectangle(::nana::rectangle{ text_pos, { whitespace_w, line_h_pixels } }, true); + + extra_space = text_selected; } else { - auto rtl_string = [this, line_h_pixels, &parser, &clr](point strpos, const wchar_t* str, std::size_t len, std::size_t str_px, unsigned glyph_front, unsigned glyph_selected){ - this->_m_draw_parse_string(parser, true, strpos, clr, str, len); - - //Draw selected part - paint::graphics graph({ glyph_selected, line_h_pixels }); - graph.typeface(this->graph_.typeface()); - graph.rectangle(true, scheme_->selection.get_color()); - - int sel_xpos = static_cast(str_px - (glyph_front + glyph_selected)); - - graph.palette(true, scheme_->selection_text.get_color()); - graph.string({-sel_xpos, 0}, str, len); - graph_.bitblt(nana::rectangle(strpos.x + sel_xpos, strpos.y, glyph_selected, line_h_pixels), graph); - }; - - const wchar_t * strbeg = linestr.c_str(); - if (a.y == b.y) + for (auto & ent : reordered) { - for (auto & ent : reordered) + const auto len = ent.end - ent.begin; + auto ent_px = graph_.text_extent_size(ent.begin, len).width; + + 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)) { - std::size_t len = ent.end - ent.begin; - unsigned str_w = graph_.text_extent_size(ent.begin, len).width; - if ((text_pos.x + static_cast(str_w) > text_area_.area.x) && (text_pos.x < text_right)) + if (send <= ent.begin || ent.end <= sbegin) { - std::size_t pos = ent.begin - strbeg + str_pos.x; - const auto str_end = pos + len; - - //NOT selected or seleceted all - if ((str_end <= a.x || pos >= b.x) || (a.x <= pos && str_end <= b.x)) - { - //selected all - if (a.x <= pos && str_end <= b.x) - { - graph_.rectangle(::nana::rectangle{ text_pos, { str_w, line_h_pixels } }, true); - graph_.palette(true, scheme_->selection_text.get_color()); - graph_.string(text_pos, ent.begin, len); - } - else - _m_draw_parse_string(parser, false, text_pos, clr, ent.begin, len); - } - else if (pos <= a.x && a.x < str_end) - { //Partial selected - int endpos = static_cast(b.x < str_end ? b.x : str_end); - std::unique_ptr pxbuf_ptr(new unsigned[len]); - unsigned * pxbuf = pxbuf_ptr.get(); - if (graph_.glyph_pixels(ent.begin, len, pxbuf)) - { - auto head_w = std::accumulate(pxbuf, pxbuf + (a.x - pos), unsigned()); - auto sel_w = std::accumulate(pxbuf + (a.x - pos), pxbuf + (endpos - pos), unsigned()); - - graph_.palette(true, clr); - if (is_right_text(ent)) - { //RTL - rtl_string(text_pos, ent.begin, len, str_w, head_w, sel_w); - } - else - { //LTR - _m_draw_parse_string(parser, false, text_pos, clr, ent.begin, a.x - pos); - - auto part_pos = text_pos; - part_pos.x += static_cast(head_w); - - //Draw selected part - graph_.rectangle(::nana::rectangle{ part_pos, { sel_w, line_h_pixels } }, true); - graph_.palette(true, scheme_->selection_text.get_color()); - graph_.string(part_pos, ent.begin + (a.x - pos), endpos - a.x); - - if (static_cast(endpos) < str_end) - { - part_pos.x += static_cast(sel_w); - _m_draw_parse_string(parser, false, part_pos, clr, ent.begin + (endpos - pos), str_end - endpos); - } - } - } - } - else if (pos <= b.x && b.x < str_end) - { //Partial selected - int endpos = b.x; - unsigned sel_w = graph_.glyph_extent_size(ent.begin, len, 0, endpos - pos).width; - - if (is_right_text(ent)) - { //RTL - graph_.palette(true, clr); - rtl_string(text_pos, ent.begin, len, str_w, 0, sel_w); - } - else - { //LTR - //Draw selected part - graph_.rectangle(::nana::rectangle{ text_pos, { sel_w, line_h_pixels } }, true); - graph_.palette(true, scheme_->selection_text.get_color()); - graph_.string(text_pos, ent.begin, endpos - pos); - - _m_draw_parse_string(parser, false, text_pos + ::nana::point(static_cast(sel_w), 0), clr, ent.begin + (endpos - pos), str_end - endpos); - } - } + //this string is not selected + _m_draw_parse_string(parser, false, text_draw_pos, clr, ent.begin, len); } - text_pos.x += static_cast(str_w); - }//end for - } - else if (a.y == str_pos.y) - { - for (auto & ent : reordered) - { - std::size_t len = ent.end - ent.begin; - unsigned str_w = graph_.text_extent_size(ent.begin, len).width; - if ((text_pos.x + static_cast(str_w) > text_area_.area.x) && (text_pos.x < text_right)) + else if (sbegin <= ent.begin && ent.end <= send) { + //this string is completed selected + pencil.write_selection(text_draw_pos, ent_px, ent.begin, len); + 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); + + std::unique_ptr pxbuf{ new unsigned[len] }; + graph_.glyph_pixels(ent.begin, len, pxbuf.get()); + + 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); - std::size_t pos = ent.begin - strbeg + str_pos.x; - if ((pos + len <= a.x) || (a.x < pos)) //Not selected or selected all - { - if (a.x < pos) - { - //Draw selected all - graph_.rectangle(::nana::rectangle{ text_pos, { str_w, line_h_pixels } }, true, static_cast(0x3399FF)); - graph_.palette(true, scheme_->selection_text.get_color()); - graph_.string(text_pos, ent.begin, len); - } - else - _m_draw_parse_string(parser, false, text_pos, clr, ent.begin, len); + if (is_right_text(ent)) + { //RTL + pencil.rtl_string(text_draw_pos, ent.begin, len, ent_px, head_px, select_px); } else - { - unsigned head_w = graph_.glyph_extent_size(ent.begin, len, 0, a.x - pos).width; - if (is_right_text(ent)) - { //RTL - rtl_string(text_pos, ent.begin, len, str_w, head_w, str_w - head_w); - } - else - { //LTR - graph_.string(text_pos, ent.begin, a.x - pos); + { //LTR + _m_draw_parse_string(parser, false, text_draw_pos, clr, ent.begin, select_pos); - ::nana::point part_pos{ text_pos.x + static_cast(head_w), text_pos.y }; + auto part_pos = text_draw_pos; + part_pos.x += static_cast(head_px); - //Draw selected part - graph_.rectangle(::nana::rectangle{ part_pos, {str_w - head_w, line_h_pixels } }, true); - graph_.palette(true, scheme_->selection_text.get_color()); - graph_.string(part_pos, ent.begin + a.x - pos, len - (a.x - pos)); - } - } - } + pencil.write_selection(part_pos, select_px, ent.begin + select_pos, select_len); - text_pos.x += static_cast(str_w); - } - - if (str_pos.y < b.y) - { - if (a.y < str_pos.y || ((a.y == str_pos.y) && (a.x <= str_pos.x ))) - graph_.rectangle(::nana::rectangle{ text_pos, { whitespace_w, line_h_pixels } }, true); - } - } - else if (b.y == str_pos.y) - { - for (auto & ent : reordered) - { - std::size_t len = ent.end - ent.begin; - unsigned str_w = graph_.text_extent_size(ent.begin, len).width; - if ((text_pos.x + static_cast(str_w) > text_area_.area.x) && (text_pos.x < text_right)) - { - std::size_t pos = ent.begin - strbeg + str_pos.x; - graph_.palette(true, clr); - if (pos + len <= b.x) - { - //Draw selected part - graph_.rectangle(::nana::rectangle{ text_pos, { str_w, line_h_pixels } }, true); - graph_.palette(true, scheme_->selection_text.get_color()); - graph_.string(text_pos, ent.begin, len); - } - else if (pos <= b.x && b.x < pos + len) - { - unsigned sel_w = graph_.glyph_extent_size(ent.begin, len, 0, b.x - pos).width; - if (is_right_text(ent)) - { //RTL - rtl_string(text_pos, ent.begin, len, str_w, 0, sel_w); - } - else + if (ent_send < ent.end) { - //draw selected part - graph_.rectangle(::nana::rectangle{ text_pos, { sel_w, line_h_pixels } }, true); - graph_.palette(true, scheme_->selection_text.get_color()); - graph_.string(text_pos, ent.begin, b.x - pos); - - _m_draw_parse_string(parser, false, text_pos + ::nana::point(static_cast(sel_w), 0), clr, ent.begin + b.x - pos, len - (b.x - pos)); + part_pos.x += static_cast(select_px); + _m_draw_parse_string(parser, false, part_pos, clr, ent_send, ent.end - ent_send); } } - else - _m_draw_parse_string(parser, false, text_pos, clr, ent.begin, len); + + extra_space = (select_pos + select_len == text_ptr->size()); } - text_pos.x += static_cast(str_w); } + 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_ptr->size(); + if (b.x != pos || text_coord.y != b.y) + { + auto whitespace_w = graph_.text_extent_size(L" ", 1).width; + graph_.rectangle(::nana::rectangle{ text_draw_pos, { whitespace_w, line_h_pixels } }, true); } } } @@ -3303,16 +3393,17 @@ namespace nana{ namespace widgets if (select_.a == select_.b) return false; - if((select_.a.y > select_.b.y) || ((select_.a.y == select_.b.y) && (select_.a.x > select_.b.x))) - { - a = select_.b; - b = select_.a; - } - else + if (select_.a < select_.b) { a = select_.a; b = select_.b; } + else + { + a = select_.b; + b = select_.a; + } + return true; } @@ -3321,42 +3412,47 @@ namespace nana{ namespace widgets points_.offset.y = y; } - unsigned text_editor::_m_char_by_pixels(const wchar_t* str, std::size_t len, unsigned * pxbuf, int str_px, int pixels, bool is_rtl) + unsigned text_editor::_m_char_by_pixels(const unicode_bidi::entity& ent, unsigned pos) { - if (graph_.glyph_pixels(str, len, pxbuf)) - { - if (is_rtl) - { //RTL - for (std::size_t u = 0; u < len; ++u) - { - auto px = static_cast(pxbuf[u]); - auto chbeg = str_px - px; - if (chbeg <= pixels && pixels < str_px) - { - if ((px < 2) || (pixels <= chbeg + (px >> 1))) - return static_cast(u + 1); + auto len = static_cast(ent.end - ent.begin); - return static_cast(u); + std::unique_ptr pxbuf(new unsigned[len]); + if (graph_.glyph_pixels(ent.begin, len, pxbuf.get())) + { + 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()); } - str_px = chbeg; + total_px = chpos; } } else { - //LTR - for (std::size_t u = 0; u < len; ++u) + for (auto p = pxbuf.get(); p != px_end; ++p) { - auto px = static_cast(pxbuf[u]); - if (pixels < px) + if (pos < *p) { - if ((px > 1) && (pixels > (px >> 1))) - return static_cast(u + 1); - return static_cast(u); + if ((*p > 1) && (pos >(*p >> 1))) + return static_cast(p - pxbuf.get()) + 1; + return static_cast(p - pxbuf.get()); } - pixels -= px; + pos -= *p; } } } + return 0; } @@ -3365,17 +3461,16 @@ namespace nana{ namespace widgets if (pos > lnstr.size()) return 0; - unicode_bidi bidi; std::vector reordered; - bidi.linestr(lnstr.data(), lnstr.size(), reordered); + unicode_bidi{}.linestr(lnstr.c_str(), lnstr.size(), reordered); - auto ch = lnstr.data() + pos; + 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 <= ch && ch <= ent.end) + if (ent.begin <= target && target <= ent.end) { if (is_right_text(ent)) { @@ -3383,10 +3478,10 @@ namespace nana{ namespace widgets //RTL std::unique_ptr pxbuf(new unsigned[len]); graph_.glyph_pixels(ent.begin, len, pxbuf.get()); - return std::accumulate(pxbuf.get() + (ch - ent.begin), pxbuf.get() + len, text_w); + return std::accumulate(pxbuf.get() + (target - ent.begin), pxbuf.get() + len, text_w); } //LTR - return text_w + _m_text_extent_size(ent.begin, ch - ent.begin).width; + return text_w + _m_text_extent_size(ent.begin, target - ent.begin).width; } else text_w += _m_text_extent_size(ent.begin, len).width; diff --git a/source/gui/widgets/slider.cpp b/source/gui/widgets/slider.cpp index 72fb50ca..e89e210f 100644 --- a/source/gui/widgets/slider.cpp +++ b/source/gui/widgets/slider.cpp @@ -1,5 +1,6 @@ #include +#include //memcpy namespace nana { @@ -13,91 +14,262 @@ namespace nana { class interior_renderer - : public renderer + : public renderer_interface { private: - virtual void background(window wd, graph_reference graph, bool isglass) + void background(window, graph_reference graph, bool transparent, const scheme& schm) override { - if(!isglass) - graph.rectangle(true, API::bgcolor(wd)); + if (!transparent) + graph.rectangle(true, schm.background); } - virtual void bar(window, graph_reference graph, const bar_t& bi) + void bar(window, graph_reference graph, const data_bar& data, const scheme& schm) override { - //draw border - ::nana::color lt(0x83, 0x90, 0x97), rb(0x9d,0xae,0xc2); - graph.frame_rectangle(bi.r, lt, lt, rb, rb); - } + auto area = data.area; - virtual void adorn(window, graph_reference graph, const adorn_t& ad) - { - auto len = static_cast(ad.bound.y - ad.bound.x); - const auto upperblock = ad.block - ad.block / 2; - - ::nana::color clr_from(0x84, 0xc5, 0xff), clr_trans(0x0f, 0x41, 0xcd), clr_to(0x6e, 0x96, 0xff); - if(ad.horizontal) + if (data.vert) { - graph.gradual_rectangle({ ad.bound.x, ad.fixedpos, len, upperblock }, clr_from, clr_trans, true); - graph.gradual_rectangle({ ad.bound.x, ad.fixedpos + static_cast(upperblock), len, ad.block - upperblock }, clr_trans, clr_to, true); + area.x = area.width / 2 - 2; + area.width = 4; } else { - graph.gradual_rectangle({ ad.fixedpos, ad.bound.x, upperblock, len }, clr_from, clr_trans, false); - graph.gradual_rectangle({ ad.fixedpos + static_cast(upperblock), ad.bound.x, ad.block - upperblock, len }, clr_trans, clr_to, false); + area.y = area.height / 2 - 2; + area.height = 4; } + + graph.rectangle(area, false, schm.color_bar); } - virtual void adorn_textbox(window, graph_reference graph, const std::string& str, const nana::rectangle & r) + void adorn(window, graph_reference graph, const data_adorn& data, const scheme& schm) override { - graph.rectangle(r, false, colors::white); - graph.string({ r.x + 2, r.y + 1 }, str, colors::white); + rectangle area{ + data.bound.x, data.fixedpos + static_cast(data.block / 2) - 1, + static_cast(data.bound.y - data.bound.x) , 2 + }; + + if (data.vert) + area.shift(); + + graph.rectangle(area, true, schm.color_adorn); + } - virtual void slider(window, graph_reference graph, const slider_t& s) + void vernier(window, graph_reference graph, const data_vernier& data, const scheme& schm) override { - nana::rectangle r{ graph.size() }; - if(s.horizontal) + if (data.vert) + _m_draw_vernier_vert(graph, data, schm); + else + _m_draw_vernier_horz(graph, data, schm); + } + + void slider(window, graph_reference graph, mouse_action mouse_act, const data_slider& data, const scheme& schm) override + { + nana::rectangle area{ graph.size() }; + + if (data.vert) { - r.x = s.pos; - r.width = s.scale; + area.y = static_cast(data.pos); + area.height = data.weight; } else { - r.y = s.pos; - r.height = s.scale; + area.x = static_cast(data.pos); + area.width = data.weight; } - graph.round_rectangle(r, 3, 3, colors::black, true, static_cast(0xf0f0f0)); + + color rgb = schm.color_slider; + if (mouse_action::normal != mouse_act) + rgb = schm.color_slider_highlighted; + + graph.frame_rectangle(area, rgb + static_cast(0x0d0d0d), 1); + graph.rectangle(area.pare_off(1), true, rgb); + + area.height /= 2; + graph.rectangle(area, true, rgb + static_cast(0x101010)); + } + private: + void _m_draw_vernier_horz(graph_reference graph, const data_vernier& data, const scheme& schm) + { + const unsigned arrow_weight = 5; + + unsigned arrow_pxbuf[] = { + 0x7F, 0x00, 0x00, 0x00, 0x00, + 0x7F, 0x7F, 0x00, 0x00, 0x00, + 0x7F, 0x7F, 0x7F, 0x00, 0x00, + 0x7F, 0x7F, 0x7F, 0x7F, 0x00, + 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, + 0x7F, 0x7F, 0x7F, 0x7F, 0x00, + 0x7F, 0x7F, 0x7F, 0x00, 0x00, + 0x7F, 0x7F, 0x00, 0x00, 0x00, + 0x7F, 0x00, 0x00, 0x00, 0x00 + }; + + const size arrow_size{ arrow_weight, 9 }; + + const auto label_size = graph.text_extent_size(data.text) + size{ schm.vernier_text_margin * 2, 0 }; + + paint::graphics graph_vern{ label_size }; + graph_vern.rectangle(true, schm.color_vernier); + + int arrow_pos; + + point label_pos{ data.position, static_cast(graph.height() - label_size.height) / 2 }; + + if (static_cast(label_size.width + arrow_weight) > data.position) + { + label_pos.x += arrow_weight; + arrow_pos = data.position; + } + else + { + label_pos.x -= label_size.width + arrow_weight; + arrow_pos = data.position - arrow_weight; + } + + graph_vern.blend(rectangle{ label_size }, graph, label_pos, 0.5); + + + unsigned arrow_color = 0x7F | schm.color_vernier.get_color().argb().value; + for (auto & color : arrow_pxbuf) + { + if (color == 0x7F) + color = arrow_color; + } + + if (label_pos.x > data.position) + { + for (::nana::size::value_type l = 0; l < arrow_size.height; ++l) + { + auto ptr = arrow_pxbuf + l * arrow_size.width; + + for (::nana::size::value_type x = 0; x < arrow_size.width / 2; ++x) + std::swap(ptr[x], ptr[(arrow_size.width - 1) - x]); + } + } + + paint::pixel_buffer pxbuf{ arrow_size.width, arrow_size.height }; + pxbuf.alpha_channel(true); + pxbuf.put(reinterpret_cast(arrow_pxbuf), arrow_size.width, arrow_size.height, 32, arrow_size.width * 4, false); + + pxbuf.paste(rectangle{ arrow_size }, graph.handle(), { arrow_pos, label_pos.y + static_cast(label_size.height - arrow_size.height) / 2 }); + + label_pos.x += static_cast(schm.vernier_text_margin); + graph.string(label_pos, data.text, schm.color_vernier_text); + } + + void _m_draw_vernier_vert(graph_reference graph, const data_vernier& data, const scheme& schm) + { + const unsigned arrow_weight = 5; + + unsigned arrow_pxbuf[] = { + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x00, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x00, + 0x00, 0x00, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x7f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, + }; + + const size arrow_size{ 9, arrow_weight}; + + const size label_size = (graph.text_extent_size(data.text) + size{ schm.vernier_text_margin * 2, 0 }).shift(); + + paint::graphics graph_vern{ label_size }; + + paint::graphics graph_horz{ size(label_size).shift() }; + graph_horz.rectangle(true, schm.color_vernier); + graph_horz.string({ static_cast(schm.vernier_text_margin), static_cast(graph_horz.height() - label_size.width) / 2 }, data.text, schm.color_vernier_text); + + paint::pixel_buffer{ graph_horz.handle(), 0, graph_horz.height() }.rotate(90, colors::white).paste(graph_vern.handle(), {}); + + int arrow_pos; + + point label_pos{ static_cast(graph.width() - label_size.width) / 2, data.position }; + + if (static_cast(label_size.height + arrow_weight) > (data.end_position - data.position)) + { + label_pos.y -= arrow_weight + label_size.height; + arrow_pos = data.position - arrow_weight; + + const unsigned line_bytes = arrow_size.width * sizeof(unsigned); + for (size::value_type l = 0; l < arrow_size.height / 2; ++l) + { + auto swap_x = arrow_pxbuf + l* arrow_size.width; + auto swap_y = arrow_pxbuf + (arrow_size.height - 1 - l) * arrow_size.width; + + unsigned tmp[9]; + std::memcpy(tmp, swap_x, line_bytes); + std::memcpy(swap_x, swap_y, line_bytes); + std::memcpy(swap_y, tmp, line_bytes); + } + } + else + { + label_pos.y += arrow_weight; + arrow_pos = data.position; + } + + graph_vern.blend(rectangle{ label_size }, graph, label_pos, 0.5); + + + unsigned arrow_color = 0x7F | schm.color_vernier.get_color().argb().value; + for (auto & color : arrow_pxbuf) + { + if (color == 0x7F) + color = arrow_color; + } + + + paint::pixel_buffer pxbuf{ arrow_size.width, arrow_size.height }; + pxbuf.alpha_channel(true); + pxbuf.put(reinterpret_cast(arrow_pxbuf), arrow_size.width, arrow_size.height, 32, arrow_size.width * 4, false); + + pxbuf.paste(rectangle{ arrow_size }, graph.handle(), { label_pos.x + static_cast(label_size.width - arrow_size.width) / 2, arrow_pos }); + + label_pos.y += static_cast(schm.vernier_text_margin); } }; - class controller + class trigger::model { + struct attrib_rep + { + seekdir seek_dir; + bool is_draw_adorn; + + unsigned vmax; + double vcur; + double adorn_pos; + + renderer_interface::data_slider slider; + }; public: - enum class style{horizontal, vertical}; enum class parts{none, bar, slider}; - typedef drawer_trigger::graph_reference graph_reference; + using graph_reference = drawer_trigger::graph_reference; - controller() + model() { other_.wd = nullptr; other_.widget = nullptr; - other_.graph = nullptr; - proto_.renderer = pat::cloneable(interior_renderer()); + proto_.renderer = pat::cloneable{interior_renderer{}}; - attr_.skdir = seekdir::bilateral; - attr_.dir = style::horizontal; + attr_.seek_dir = seekdir::bilateral; + + attr_.is_draw_adorn = false; attr_.vcur = 0; attr_.vmax = 10; - attr_.slider_scale = 8; - attr_.border = 1; - attr_.is_draw_adorn = false; + + attr_.slider.vert = false; + attr_.slider.border_weight = 1; + attr_.slider.pos = 0; + attr_.slider.weight = 8; } - void seek(seekdir sd) + void seek_direction(seekdir sd) { - attr_.skdir = sd; + attr_.seek_dir = sd; } window handle() const @@ -105,86 +277,68 @@ namespace nana return other_.wd; } - void attached(nana::slider& wd, graph_reference graph) + void attached(nana::slider& wdg, graph_reference) { - other_.wd = wd.handle(); - other_.widget = &wd; + other_.wd = wdg.handle(); + other_.widget = &wdg; - other_.graph = &graph; _m_mk_slider_pos_by_value(); } - void detached() - { - other_.graph = nullptr; - } - - pat::cloneable& ext_renderer() + pat::cloneable& renderer() { return proto_.renderer; } - void ext_renderer(const pat::cloneable& rd) + void vernier(std::function vernier_string) { - proto_.renderer = rd; + proto_.vernier = vernier_string; } - void ext_provider(const pat::cloneable& pd) + void draw(graph_reference graph) { - proto_.provider = pd; - } - - void draw() - { - if(other_.graph && !other_.graph->size().empty()) + if(!graph.size().empty()) { - bool is_transparent = (bground_mode::basic == API::effects_bground_mode(other_.wd)); - proto_.renderer->background(other_.wd, *other_.graph, is_transparent); - _m_draw_objects(); + proto_.renderer->background(other_.wd, graph, (bground_mode::basic == API::effects_bground_mode(other_.wd)), other_.widget->scheme()); + _m_draw_elements(graph); } } - void vertical(bool v) + const attrib_rep & attribute() const { - auto dir = (v ? style::vertical : style::horizontal); + return attr_; + } - if(dir != attr_.dir) + bool vertical(bool vert) + { + if (vert != attr_.slider.vert) { - attr_.dir = dir; + attr_.slider.vert = vert; _m_mk_slider_pos_by_value(); - this->draw(); + return true; } + return false; } - bool vertical() const - { - return (style::vertical == attr_.dir); - } - - void vmax(unsigned m) + void maximum(unsigned m) { if(m == 0) m = 1; - if(attr_.vmax != m) + if (attr_.vmax == m) + return; + + attr_.vmax = m; + if(attr_.vcur > m) { - attr_.vmax = m; - if(attr_.vcur > m) - { - attr_.vcur = m; - _m_emit_value_changed(); - } - - _m_mk_slider_pos_by_value(); - draw(); + attr_.vcur = m; + _m_emit_value_changed(); } + + _m_mk_slider_pos_by_value(); + API::refresh_window(other_.wd); } - unsigned vmax() const - { - return attr_.vmax; - } - - void vcur(unsigned v) + bool vcur(unsigned v) { if(attr_.vmax < v) v = attr_.vmax; @@ -193,39 +347,36 @@ namespace nana { attr_.vcur = v; this->_m_mk_slider_pos_by_value(); - draw(); + return true; } - } - - unsigned vcur() const - { - return static_cast(attr_.vcur); + return false; } void resize() { _m_mk_slider_pos_by_value(); - attr_.adorn_pos = attr_.pos; + attr_.adorn_pos = attr_.slider.pos; } parts seek_where(::nana::point pos) const { nana::rectangle r = _m_bar_area(); - if(style::vertical == attr_.dir) + + if (attr_.slider.vert) { std::swap(pos.x, pos.y); std::swap(r.width, r.height); } int sdpos = _m_slider_pos(); - if (sdpos <= pos.x && pos.x < sdpos + static_cast(attr_.slider_scale)) + if (sdpos <= pos.x && pos.x < sdpos + static_cast(attr_.slider.weight)) return parts::slider; - sdpos = static_cast(attr_.slider_scale) / 2; + sdpos = static_cast(attr_.slider.weight) / 2; if (sdpos <= pos.x && pos.x < sdpos + static_cast(r.width)) { - if(pos.y < r.y + static_cast(r.height)) + if(pos.y < r.bottom()) return parts::bar; } return parts::none; @@ -235,49 +386,50 @@ namespace nana //move the slider to a position where a mouse click on WhereBar. bool set_slider_pos(::nana::point pos) { - if(style::vertical == attr_.dir) + if(attr_.slider.vert) std::swap(pos.x, pos.y); pos.x -= _m_slider_refpos(); if(pos.x < 0) return false; - if(pos.x > static_cast(_m_scale())) - pos.x = static_cast(_m_scale()); + if(pos.x > static_cast(_m_range())) + pos.x = static_cast(_m_range()); - double attr_pos = attr_.pos; + auto attr_pos = attr_.slider.pos; double dx = _m_evaluate_by_seekdir(pos.x); - attr_.pos = dx; + attr_.slider.pos = dx; attr_.adorn_pos = dx; _m_mk_slider_value_by_pos(); - return (attr_.pos != attr_pos); + return (attr_.slider.pos != attr_pos); } void set_slider_refpos(::nana::point pos) { - if(style::vertical == attr_.dir) + if (attr_.slider.vert) std::swap(pos.x, pos.y); - slider_state_.trace = slider_state_.TraceCapture; - slider_state_.snap_pos = static_cast(attr_.pos); + slider_state_.mouse_state = ::nana::mouse_action::pressed; + slider_state_.snap_pos = static_cast(attr_.slider.pos); slider_state_.refpos = pos; - API::capture_window(other_.wd, true); + API::set_capture(other_.wd, true); } bool release_slider() { - if(slider_state_.trace == slider_state_.TraceCapture) + if(::nana::mouse_action::pressed == slider_state_.mouse_state) { - API::capture_window(other_.wd, false); - if(other_.wd != API::find_window(API::cursor_position())) + API::release_capture(other_.wd); + + if (other_.wd != API::find_window(API::cursor_position())) { - slider_state_.trace = slider_state_.TraceNone; + slider_state_.mouse_state = ::nana::mouse_action::normal; attr_.is_draw_adorn = false; } else - slider_state_.trace = slider_state_.TraceOver; + slider_state_.mouse_state = ::nana::mouse_action::hovered; _m_mk_slider_value_by_pos(); _m_mk_slider_pos_by_value(); @@ -288,19 +440,18 @@ namespace nana bool if_trace_slider() const { - return (slider_state_.trace == slider_state_.TraceCapture); + return (::nana::mouse_action::pressed == slider_state_.mouse_state); } bool move_slider(const ::nana::point& pos) { - int mpos = (style::horizontal == attr_.dir ? pos.x : pos.y); - int adorn_pos = slider_state_.snap_pos + (mpos - slider_state_.refpos.x); + int adorn_pos = slider_state_.snap_pos + (attr_.slider.vert ? pos.y : pos.x) - slider_state_.refpos.x; if (adorn_pos > 0) { - int scale = static_cast(_m_scale()); - if (adorn_pos > scale) - adorn_pos = scale; + int range = static_cast(_m_range()); + if (adorn_pos > range) + adorn_pos = range; } else adorn_pos = 0; @@ -308,9 +459,9 @@ namespace nana double dstpos = _m_evaluate_by_seekdir(adorn_pos); attr_.is_draw_adorn = true; - if(dstpos != attr_.pos) + if(dstpos != attr_.slider.pos) { - attr_.pos = dstpos; + attr_.slider.pos = dstpos; attr_.adorn_pos = dstpos; return true; } @@ -319,11 +470,11 @@ namespace nana bool move_adorn(const ::nana::point& pos) { - double xpos = (style::horizontal == attr_.dir ? pos.x : pos.y); + double xpos = (attr_.slider.vert ? pos.y : pos.x) - _m_slider_refpos(); - xpos -= _m_slider_refpos(); - if(xpos > static_cast(_m_scale())) - xpos = static_cast(_m_scale()); + auto range = static_cast(_m_range()); + if (xpos > range) + xpos = range; int adorn_pos = static_cast(attr_.adorn_pos); xpos = _m_evaluate_by_seekdir(xpos); @@ -331,8 +482,8 @@ namespace nana attr_.adorn_pos = xpos; attr_.is_draw_adorn = true; - if(slider_state_.trace == slider_state_.TraceNone) - slider_state_.trace = slider_state_.TraceOver; + if (::nana::mouse_action::normal == slider_state_.mouse_state) + slider_state_.mouse_state = ::nana::mouse_action::hovered; return (adorn_pos != static_cast(xpos)); } @@ -353,7 +504,8 @@ namespace nana if (cmpvalue != value) { _m_mk_slider_pos_by_value(); - draw(); + API::refresh_window(other_.wd); + _m_emit_value_changed(); } @@ -369,14 +521,14 @@ namespace nana { //Test if the slider is captured, the operation should be ignored. Because the mouse_leave always be generated even through //the slider is captured. - if(slider_state_.trace == slider_state_.TraceCapture && (nana::API::capture_window() == this->other_.wd)) + if((::nana::mouse_action::pressed == slider_state_.mouse_state) && (API::capture_window() == this->other_.wd)) return false; - slider_state_.trace = slider_state_.TraceNone; + slider_state_.mouse_state = ::nana::mouse_action::normal; attr_.is_draw_adorn = false; - if(attr_.adorn_pos != attr_.pos) + if(attr_.adorn_pos != attr_.slider.pos) { - attr_.adorn_pos = attr_.pos; + attr_.adorn_pos = attr_.slider.pos; return true; } return false; @@ -385,72 +537,65 @@ namespace nana private: void _m_emit_value_changed() const { - other_.widget->events().value_changed.emit(::nana::arg_slider{ *other_.widget }); + other_.widget->events().value_changed.emit(::nana::arg_slider{ *other_.widget }, other_.widget->handle()); } - nana::rectangle _m_bar_area() const + ::nana::rectangle _m_bar_area() const { - auto sz = other_.graph->size(); - nana::rectangle r{ sz }; - if(style::horizontal == attr_.dir) + auto sz = other_.widget->size(); + + nana::rectangle area{ sz }; + if (attr_.slider.vert) { - r.x = attr_.slider_scale / 2 - attr_.border; - r.width = (static_cast(sz.width) > (r.x << 1) ? sz.width - (r.x << 1) : 0); + area.y = attr_.slider.weight / 2 - attr_.slider.border_weight; + area.height = (static_cast(sz.height) > (area.y << 1) ? sz.height - (area.y << 1) : 0); } else { - r.y = attr_.slider_scale / 2 - attr_.border; - r.height = (static_cast(sz.height) > (r.y << 1) ? sz.height - (r.y << 1) : 0); + area.x = attr_.slider.weight / 2 - attr_.slider.border_weight; + area.width = (static_cast(sz.width) > (area.x << 1) ? sz.width - (area.x << 1) : 0); } - return r; + return area; } - unsigned _m_scale() const + unsigned _m_range() const { nana::rectangle r = _m_bar_area(); - return ((style::horizontal == attr_.dir ? r.width : r.height) - attr_.border * 2); + return (attr_.slider.vert ? r.height : r.width) - attr_.slider.border_weight * 2; } double _m_evaluate_by_seekdir(double pos) const { - switch(attr_.skdir) + if (seekdir::bilateral != attr_.seek_dir) { - case seekdir::backward: - if(pos < attr_.pos) - pos = attr_.pos; - break; - case seekdir::forward: - if(pos > attr_.pos) - pos = attr_.pos; - break; - default: - break; + if ((seekdir::backward == attr_.seek_dir) == (pos < attr_.slider.pos)) + pos = attr_.slider.pos; } return (pos < 0 ? 0 : pos); } int _m_slider_refpos() const { - return static_cast(attr_.slider_scale / 2); + return static_cast(attr_.slider.weight / 2); } int _m_slider_pos() const { - return static_cast(_m_scale() * attr_.vcur / attr_.vmax); + return static_cast(_m_range() * attr_.vcur / attr_.vmax); } void _m_mk_slider_value_by_pos() { - if(_m_scale()) + auto range = _m_range(); + if (range) { auto cmpvalue = static_cast(attr_.vcur); - if (style::vertical == attr_.dir) - { - double scl = _m_scale(); - attr_.vcur = (scl - attr_.pos) * attr_.vmax / scl; - } + if (attr_.slider.vert) + attr_.vcur = (range - attr_.slider.pos) * attr_.vmax; else - attr_.vcur = (attr_.pos * attr_.vmax / _m_scale()); + attr_.vcur = (attr_.slider.pos * attr_.vmax); + + attr_.vcur /= range; if (cmpvalue != static_cast(attr_.vcur)) _m_emit_value_changed(); } @@ -458,225 +603,195 @@ namespace nana void _m_mk_slider_pos_by_value() { - attr_.pos = double(_m_scale()) * attr_.vcur / attr_.vmax; + const auto range = _m_range(); + attr_.slider.pos = double(range) * attr_.vcur / attr_.vmax; - if (style::vertical == attr_.dir) - attr_.pos = _m_scale() - attr_.pos; + if (attr_.slider.vert) + attr_.slider.pos = range - attr_.slider.pos; - if(slider_state_.trace == slider_state_.TraceNone) - attr_.adorn_pos = attr_.pos; + if(::nana::mouse_action::normal == slider_state_.mouse_state) + attr_.adorn_pos = attr_.slider.pos; } unsigned _m_value_by_pos(double pos) const { - if(_m_scale()) - return static_cast(pos * attr_.vmax / _m_scale()); - return 0; + const auto range = _m_range(); + + if (0 == range) + return 0; + + return static_cast((attr_.slider.vert ? range - pos : pos) * attr_.vmax / range); } - void _m_draw_objects() + void _m_draw_elements(graph_reference graph) { - renderer::bar_t bar; + auto & scheme = other_.widget->scheme(); - bar.horizontal = (style::horizontal == attr_.dir); - bar.border_size = attr_.border; - bar.r = _m_bar_area(); + renderer_interface::data_bar bar; - if (bar.r.empty()) + bar.vert = attr_.slider.vert; + bar.border_weight = attr_.slider.border_weight; + bar.area = _m_bar_area(); + + if (bar.area.empty()) return; - proto_.renderer->bar(other_.wd, *other_.graph, bar); + proto_.renderer->bar(other_.wd, graph, bar, scheme); //adorn - renderer::adorn_t adorn; - adorn.horizontal = bar.horizontal; - if (adorn.horizontal) + renderer_interface::data_adorn adorn; + adorn.vert = bar.vert; + if (adorn.vert) { - adorn.bound.x = bar.r.x + attr_.border; - adorn.bound.y = adorn.bound.x + static_cast(attr_.adorn_pos); + adorn.bound.x = static_cast(attr_.adorn_pos + attr_.slider.border_weight + bar.area.y); + adorn.bound.y = static_cast(graph.height()) - static_cast(attr_.slider.border_weight + bar.area.y); + //adorn.bound.x = } else { - adorn.bound.y = static_cast(other_.graph->height()) - static_cast(attr_.border + bar.r.y); - adorn.bound.x = static_cast(attr_.adorn_pos + attr_.border + bar.r.y); + adorn.bound.x = bar.area.x + attr_.slider.border_weight; + adorn.bound.y = adorn.bound.x + static_cast(attr_.adorn_pos); } - adorn.vcur_scale = static_cast(attr_.pos); - adorn.block = (bar.horizontal ? bar.r.height : bar.r.width) - attr_.border * 2; - adorn.fixedpos = static_cast((bar.horizontal ? bar.r.y : bar.r.x) + attr_.border); - proto_.renderer->adorn(other_.wd, *other_.graph, adorn); + adorn.vcur_scale = static_cast(attr_.slider.pos); + adorn.block = (bar.vert ? bar.area.width : bar.area.height) - attr_.slider.border_weight * 2; + adorn.fixedpos = static_cast((bar.vert ? bar.area.x : bar.area.y) + attr_.slider.border_weight); - _m_draw_slider(); + proto_.renderer->adorn(other_.wd, graph, adorn, scheme); + + //Draw slider + proto_.renderer->slider(other_.wd, graph, slider_state_.mouse_state, attr_.slider, scheme); //adorn textbox - if(proto_.provider && attr_.is_draw_adorn) + if (proto_.vernier && attr_.is_draw_adorn) { - unsigned vadorn = _m_value_by_pos(attr_.adorn_pos); - auto str = proto_.provider->adorn_trace(attr_.vmax, vadorn); - if(str.size()) - { - nana::size ts = other_.graph->text_extent_size(str); - ts.width += 6; - ts.height += 2; + renderer_interface::data_vernier vern; + vern.vert = attr_.slider.vert; + vern.knob_weight = attr_.slider.weight; - int x, y; - const int room = static_cast(attr_.adorn_pos); - if(bar.horizontal) - { - y = adorn.fixedpos + static_cast(adorn.block - ts.height) / 2; - x = (room > static_cast(ts.width + 2) ? room - static_cast(ts.width + 2) : room + 2) + _m_slider_refpos(); - } - else - { - x = (other_.graph->width() - ts.width) / 2; - y = (room > static_cast(ts.height + 2) ? room - static_cast(ts.height + 2) : room + 2) + _m_slider_refpos(); - } - proto_.renderer->adorn_textbox(other_.wd, *other_.graph, str, {x, y, ts.width, ts.height}); + auto vadorn = _m_value_by_pos(attr_.adorn_pos); + proto_.vernier(attr_.vmax, vadorn).swap(vern.text); + if(vern.text.size()) + { + vern.position = adorn.bound.x; + if (!adorn.vert) + vern.position += static_cast(attr_.adorn_pos); + + vern.end_position = adorn.bound.y; + proto_.renderer->vernier(other_.wd, graph, vern, scheme); } } } - - void _m_draw_slider() - { - renderer::slider_t s; - s.pos = static_cast(attr_.pos); - s.horizontal = (style::horizontal == attr_.dir); - s.scale = attr_.slider_scale; - s.border = attr_.border; - proto_.renderer->slider(other_.wd, *other_.graph, s); - } private: + attrib_rep attr_; + struct other_tag { window wd; nana::slider * widget; - paint::graphics * graph; }other_; struct prototype_tag { - pat::cloneable renderer; - pat::cloneable provider; + pat::cloneable renderer; + std::function vernier; }proto_; - struct attr_tag - { - seekdir skdir; - style dir; - unsigned border; - unsigned vmax; - double vcur; - double pos; - bool is_draw_adorn; - double adorn_pos; - unsigned slider_scale; - }attr_; - struct slider_state_tag { - enum t{TraceNone, TraceOver, TraceCapture}; - - t trace; //true if the mouse press on slider. int snap_pos; - nana::point refpos; //a point for slider when the mouse was clicking on slider. - - slider_state_tag(): trace(TraceNone){} + ::nana::point refpos; //a point for slider when the mouse was clicking on slider. + ::nana::mouse_action mouse_state{ ::nana::mouse_action::normal }; }slider_state_; }; //class trigger trigger::trigger() - : impl_(new controller_t) + : model_ptr_(new model) {} trigger::~trigger() { - delete impl_; + delete model_ptr_; } - trigger::controller_t* trigger::ctrl() const + auto trigger::get_model() const -> model* { - return impl_; + return model_ptr_; } void trigger::attached(widget_reference widget, graph_reference graph) { - impl_->attached(static_cast(widget), graph); + model_ptr_->attached(static_cast< ::nana::slider&>(widget), graph); } - void trigger::detached() + void trigger::refresh(graph_reference graph) { - impl_->detached(); + model_ptr_->draw(graph); } - void trigger::refresh(graph_reference) + void trigger::mouse_down(graph_reference graph, const arg_mouse& arg) { - impl_->draw(); - } - - void trigger::mouse_down(graph_reference, const arg_mouse& arg) - { - using parts = controller_t::parts; - auto what = impl_->seek_where(arg.pos); + using parts = model::parts; + auto what = model_ptr_->seek_where(arg.pos); if(parts::bar == what || parts::slider == what) { - bool mkdir = impl_->set_slider_pos(arg.pos); - impl_->set_slider_refpos(arg.pos); - if(mkdir) + bool updated = model_ptr_->set_slider_pos(arg.pos); + model_ptr_->set_slider_refpos(arg.pos); + if (updated) { - impl_->draw(); - API::lazy_refresh(); + model_ptr_->draw(graph); + API::dev::lazy_refresh(); } } } - void trigger::mouse_up(graph_reference, const arg_mouse&) + void trigger::mouse_up(graph_reference graph, const arg_mouse&) { - bool mkdraw = impl_->release_slider(); - if(mkdraw) + if (model_ptr_->release_slider()) { - impl_->draw(); - API::lazy_refresh(); + model_ptr_->draw(graph); + API::dev::lazy_refresh(); } } - void trigger::mouse_move(graph_reference, const arg_mouse& arg) + void trigger::mouse_move(graph_reference graph, const arg_mouse& arg) { - bool mkdraw = false; - if(impl_->if_trace_slider()) + bool updated = false; + if (model_ptr_->if_trace_slider()) { - mkdraw = impl_->move_slider(arg.pos); + updated = model_ptr_->move_slider(arg.pos); + updated |= model_ptr_->set_slider_pos(arg.pos); } else { - auto what = impl_->seek_where(arg.pos); - if(controller_t::parts::none != what) - mkdraw = impl_->move_adorn(arg.pos); + if (model::parts::none != model_ptr_->seek_where(arg.pos)) + updated = model_ptr_->move_adorn(arg.pos); else - mkdraw = impl_->reset_adorn(); + updated = model_ptr_->reset_adorn(); } - if(mkdraw) + if (updated) { - impl_->draw(); - API::lazy_refresh(); + model_ptr_->draw(graph); + API::dev::lazy_refresh(); } } - void trigger::mouse_leave(graph_reference, const arg_mouse&) + void trigger::mouse_leave(graph_reference graph, const arg_mouse&) { - if(impl_->reset_adorn()) + if (model_ptr_->reset_adorn()) { - impl_->draw(); - API::lazy_refresh(); + model_ptr_->draw(graph); + API::dev::lazy_refresh(); } } - void trigger::resized(graph_reference, const arg_resized&) + void trigger::resized(graph_reference graph, const arg_resized&) { - impl_->resize(); - impl_->draw(); - API::lazy_refresh(); + model_ptr_->resize(); + model_ptr_->draw(graph); + API::dev::lazy_refresh(); } //end class trigger }//end namespace slider @@ -695,86 +810,81 @@ namespace nana create(wd, r, visible); } - void slider::seek(slider::seekdir sd) + void slider::seek(seekdir sd) { - get_drawer_trigger().ctrl()->seek(sd); + get_drawer_trigger().get_model()->seek_direction(sd); } void slider::vertical(bool v) { - get_drawer_trigger().ctrl()->vertical(v); - API::update_window(this->handle()); + if(get_drawer_trigger().get_model()->vertical(v)) + API::refresh_window(this->handle()); } bool slider::vertical() const { - return get_drawer_trigger().ctrl()->vertical(); + return get_drawer_trigger().get_model()->attribute().slider.vert; } - void slider::vmax(unsigned m) + void slider::maximum(unsigned m) { - if(this->handle()) - { - get_drawer_trigger().ctrl()->vmax(m); - API::update_window(handle()); - } + get_drawer_trigger().get_model()->maximum(m); } - unsigned slider::vmax() const + unsigned slider::maximum() const { - if(handle()) - return get_drawer_trigger().ctrl()->vmax(); - return 0; + if (empty()) + return 0; + + return get_drawer_trigger().get_model()->attribute().vmax; } void slider::value(unsigned v) { if(handle()) { - get_drawer_trigger().ctrl()->vcur(v); - API::update_window(handle()); + if(get_drawer_trigger().get_model()->vcur(v)) + API::refresh_window(handle()); } } unsigned slider::value() const { - if(handle()) - return get_drawer_trigger().ctrl()->vcur(); - return 0; + if (empty()) + return 0; + + return static_cast(get_drawer_trigger().get_model()->attribute().vcur); } unsigned slider::move_step(bool forward) { - if(handle()) - { - drawerbase::slider::controller* ctrl = this->get_drawer_trigger().ctrl(); - unsigned val = ctrl->move_step(forward); - if(val != ctrl->vcur()) - API::update_window(handle()); - return val; - } - return 0; + if (empty()) + return 0; + + return this->get_drawer_trigger().get_model()->move_step(forward); } unsigned slider::adorn() const { - if(empty()) return 0; - return get_drawer_trigger().ctrl()->adorn(); + if(empty()) + return 0; + + return get_drawer_trigger().get_model()->adorn(); } - pat::cloneable& slider::ext_renderer() + const pat::cloneable& slider::renderer() { - return get_drawer_trigger().ctrl()->ext_renderer(); + return get_drawer_trigger().get_model()->renderer(); } - void slider::ext_renderer(const pat::cloneable& di) + void slider::renderer(const pat::cloneable& rd) { - get_drawer_trigger().ctrl()->ext_renderer(di); + get_drawer_trigger().get_model()->renderer() = rd; } - void slider::ext_provider(const pat::cloneable& pi) + void slider::vernier(std::function vernier_string) { - get_drawer_trigger().ctrl()->ext_provider(pi); + get_drawer_trigger().get_model()->vernier(vernier_string); } void slider::transparent(bool enabled) diff --git a/source/gui/widgets/spinbox.cpp b/source/gui/widgets/spinbox.cpp index 880fa7f1..961ddefa 100644 --- a/source/gui/widgets/spinbox.cpp +++ b/source/gui/widgets/spinbox.cpp @@ -38,7 +38,7 @@ namespace nana void text_changed() override { - widget_.events().text_changed.emit(::nana::arg_spinbox{ widget_ }); + widget_.events().text_changed.emit(::nana::arg_spinbox{ widget_ }, widget_.handle()); } private: ::nana::spinbox & widget_; @@ -162,7 +162,7 @@ namespace nana { for (auto & s : initlist) { - texts_.emplace_back(::nana::charset(s, ::nana::unicode::utf8)); + texts_.emplace_back(std::string{ s }); } } @@ -351,7 +351,8 @@ namespace nana { if (!pressed) { - API::capture_window(editor_->window_handle(), false); + API::release_capture(editor_->window_handle()); + timer_.stop(); timer_.interval(600); } @@ -361,7 +362,7 @@ namespace nana //Spins the value when mouse button is released if (pressed) { - API::capture_window(editor_->window_handle(), true); + API::set_capture(editor_->window_handle(), true); range_->spin(buttons::increase == spin_stated_); reset_text(); timer_.start(); @@ -421,9 +422,9 @@ namespace nana return; if (API::is_focus_ready(editor_->window_handle())) - editor_->text(to_wstring(range_->value())); + editor_->text(to_wstring(range_->value()), false); else - editor_->text(to_wstring(modifier_.prefix + range_->value() + modifier_.suffix)); + editor_->text(to_wstring(modifier_.prefix + range_->value() + modifier_.suffix), false); _m_draw_spins(spin_stated_); } @@ -518,43 +519,43 @@ namespace nana impl_->render(); } - void drawer::focus(graph_reference, const arg_focus& arg) + void drawer::focus(graph_reference, const arg_focus&) { impl_->reset_text(); impl_->render(); impl_->editor()->reset_caret(); - API::lazy_refresh(); + API::dev::lazy_refresh(); } void drawer::mouse_wheel(graph_reference, const arg_wheel& arg) { impl_->mouse_wheel(arg.upwards); impl_->editor()->reset_caret(); - API::lazy_refresh(); + API::dev::lazy_refresh(); } void drawer::mouse_down(graph_reference, const arg_mouse& arg) { if (impl_->mouse_button(arg, true)) - API::lazy_refresh(); + API::dev::lazy_refresh(); } void drawer::mouse_up(graph_reference, const arg_mouse& arg) { if (impl_->mouse_button(arg, false)) - API::lazy_refresh(); + API::dev::lazy_refresh(); } void drawer::mouse_move(graph_reference, const arg_mouse& arg) { if (impl_->mouse_move(arg.left_button, arg.pos)) - API::lazy_refresh(); + API::dev::lazy_refresh(); } void drawer::mouse_leave(graph_reference, const arg_mouse&) { impl_->render(); - API::lazy_refresh(); + API::dev::lazy_refresh(); } void drawer::key_press(graph_reference, const arg_keyboard& arg) @@ -563,7 +564,7 @@ namespace nana { impl_->editor()->reset_caret(); impl_->draw_spins(); - API::lazy_refresh(); + API::dev::lazy_refresh(); } } @@ -574,16 +575,16 @@ namespace nana if (!impl_->value(to_utf8(impl_->editor()->text()))) impl_->draw_spins(); - API::lazy_refresh(); + API::dev::lazy_refresh(); } } - void drawer::resized(graph_reference graph, const arg_resized& arg) + void drawer::resized(graph_reference, const arg_resized&) { impl_->reset_text_area(); impl_->render(); impl_->editor()->reset_caret(); - API::lazy_refresh(); + API::dev::lazy_refresh(); } } }//end namespace drawerbase @@ -696,7 +697,7 @@ namespace nana auto editor = get_drawer_trigger().impl()->editor(); if (editor) { - editor->text(to_wstring(text)); + editor->text(to_wstring(text), false); API::refresh_window(*this); } } diff --git a/source/gui/widgets/tabbar.cpp b/source/gui/widgets/tabbar.cpp index 8aa7037e..04426064 100644 --- a/source/gui/widgets/tabbar.cpp +++ b/source/gui/widgets/tabbar.cpp @@ -43,7 +43,7 @@ namespace nana : public item_renderer { private: - virtual void background(graph_reference graph, const nana::rectangle& r, const ::nana::color& bgcolor) + virtual void background(graph_reference graph, const nana::rectangle&, const ::nana::color& bgcolor) { if(bgcolor_ != bgcolor) { @@ -59,7 +59,6 @@ namespace nana virtual void item(graph_reference graph, const item_t& m, bool active, state_t sta) { - //* const nana::rectangle & r = m.r; color bgcolor; color blcolor; @@ -961,7 +960,8 @@ namespace nana auto bgcolor = API::bgcolor(basis_.wd); auto fgcolor = API::fgcolor(basis_.wd); - item_renderer::item_t m{ ::nana::rectangle{ basis_.graph->size() } }; + item_renderer::item_t m; + m.r = ::nana::rectangle{ basis_.graph->size() }; basis_.renderer->background(*basis_.graph, m.r, bgcolor); @@ -1255,7 +1255,7 @@ namespace nana if(false == layouter_->active_by_trace()) layouter_->toolbox_answer(arg); layouter_->render(); - API::lazy_refresh(); + API::dev::lazy_refresh(); } } @@ -1266,7 +1266,7 @@ namespace nana if(rd) { layouter_->render(); - API::lazy_refresh(); + API::dev::lazy_refresh(); } } @@ -1275,7 +1275,7 @@ namespace nana if(layouter_->trace(arg.pos.x, arg.pos.y)) { layouter_->render(); - API::lazy_refresh(); + API::dev::lazy_refresh(); } } @@ -1284,7 +1284,7 @@ namespace nana if(layouter_->leave()) { layouter_->render(); - API::lazy_refresh(); + API::dev::lazy_refresh(); } } //end class trigger @@ -1535,7 +1535,7 @@ namespace nana return; refresh(graph); - API::lazy_refresh(); + API::dev::lazy_refresh(); } void driver::mouse_leave(graph_reference graph, const arg_mouse&) @@ -1544,7 +1544,7 @@ namespace nana return; refresh(graph); - API::lazy_refresh(); + API::dev::lazy_refresh(); } void driver::mouse_down(graph_reference graph, const arg_mouse&) @@ -1559,10 +1559,10 @@ namespace nana model_->show_attached_window(); refresh(graph); - API::lazy_refresh(); + API::dev::lazy_refresh(); event_arg arg; - model_->widget_ptr()->events().selected.emit(arg); + model_->widget_ptr()->events().selected.emit(arg, model_->widget_ptr()->handle()); } } //end class driver @@ -1701,7 +1701,7 @@ namespace nana if (selection_changed && (active_pos != npos)) { event_arg arg; - events().selected.emit(arg); + events().selected.emit(arg, handle()); } } //end class tabbar diff --git a/source/gui/widgets/textbox.cpp b/source/gui/widgets/textbox.cpp index a4d4d499..12f42872 100644 --- a/source/gui/widgets/textbox.cpp +++ b/source/gui/widgets/textbox.cpp @@ -13,7 +13,6 @@ #include #include #include -#include namespace nana { @@ -31,18 +30,18 @@ namespace drawerbase { void event_agent::first_change() { - widget_.events().first_change.emit(::nana::arg_textbox{ widget_, text_position_ }); + widget_.events().first_change.emit(::nana::arg_textbox{ widget_, text_position_ }, widget_); } void event_agent::text_changed() { - widget_.events().text_changed.emit(::nana::arg_textbox{ widget_, text_position_ }); + widget_.events().text_changed.emit(::nana::arg_textbox{ widget_, text_position_ }, widget_); } void event_agent::text_exposed(const std::vector& text_pos) { ::nana::arg_textbox arg(widget_, text_pos); - widget_.events().text_exposed.emit(arg); + widget_.events().text_exposed.emit(arg, widget_); } //end class event_agent @@ -88,22 +87,17 @@ namespace drawerbase { editor_ = nullptr; } - void drawer::refresh(graph_reference graph) + void drawer::refresh(graph_reference) { editor_->render(API::is_focus_ready(*widget_)); } void drawer::focus(graph_reference graph, const arg_focus& arg) { - refresh(graph); - if (!editor_->attr().multi_lines && arg.getting) - { - editor_->select(true); - editor_->move_caret_end(); - } - editor_->show_caret(arg.getting); - editor_->reset_caret(); - API::lazy_refresh(); + if (!editor_->focus_changed(arg)) + refresh(graph); + + API::dev::lazy_refresh(); } void drawer::mouse_down(graph_reference, const arg_mouse& arg) @@ -111,20 +105,20 @@ namespace drawerbase { if (editor_->mouse_pressed(arg)) { editor_->render(true); - API::lazy_refresh(); + API::dev::lazy_refresh(); } } void drawer::mouse_move(graph_reference, const arg_mouse& arg) { if(editor_->mouse_move(arg.left_button, arg.pos)) - API::lazy_refresh(); + API::dev::lazy_refresh(); } - void drawer::mouse_up(graph_reference graph, const arg_mouse& arg) + void drawer::mouse_up(graph_reference, const arg_mouse& arg) { if(editor_->mouse_pressed(arg)) - API::lazy_refresh(); + API::dev::lazy_refresh(); } void drawer::mouse_wheel(graph_reference, const arg_wheel& arg) @@ -132,20 +126,20 @@ namespace drawerbase { if(editor_->scroll(arg.upwards, true)) { editor_->reset_caret(); - API::lazy_refresh(); + API::dev::lazy_refresh(); } } void drawer::mouse_enter(graph_reference, const arg_mouse&) { if(editor_->mouse_enter(true)) - API::lazy_refresh(); + API::dev::lazy_refresh(); } void drawer::mouse_leave(graph_reference, const arg_mouse&) { if(editor_->mouse_enter(false)) - API::lazy_refresh(); + API::dev::lazy_refresh(); } void drawer::key_press(graph_reference, const arg_keyboard& arg) @@ -153,14 +147,14 @@ namespace drawerbase { if(editor_->respond_key(arg)) { editor_->reset_caret(); - API::lazy_refresh(); + API::dev::lazy_refresh(); } } void drawer::key_char(graph_reference, const arg_keyboard& arg) { if (editor_->respond_char(arg)) - API::lazy_refresh(); + API::dev::lazy_refresh(); } void drawer::resized(graph_reference graph, const arg_resized& arg) @@ -168,7 +162,7 @@ namespace drawerbase { _m_text_area(arg.width, arg.height); refresh(graph); editor_->reset_caret(); - API::lazy_refresh(); + API::dev::lazy_refresh(); } void drawer::typeface_changed(graph_reference graph) @@ -260,13 +254,14 @@ namespace drawerbase { return *this; } - textbox& textbox::reset(const std::string& str) + textbox& textbox::reset(const std::string& str, bool end_caret) { internal_scope_guard lock; auto editor = get_drawer_trigger().editor(); if (editor) - { - editor->text(to_wstring(str)); + { + editor->text(to_wstring(str), end_caret); + editor->textbase().reset(); API::update_window(this->handle()); } @@ -398,8 +393,12 @@ namespace drawerbase { { internal_scope_guard lock; auto editor = get_drawer_trigger().editor(); - if(editor && editor->multi_lines(ml)) - API::update_window(handle()); + if (editor && editor->multi_lines(ml)) + { + auto wd = handle(); + API::eat_tabstop(wd, ml); //textbox handles the Tab pressing when it is multi-line. + API::update_window(wd); + } return *this; } @@ -495,11 +494,7 @@ namespace drawerbase { auto s = _m_caption(); if (s.empty()) return 0; - std::stringstream ss; - int value; - ss << to_utf8(s); - ss >> value; - return value; + return std::stoi(s, nullptr, 0); } double textbox::to_double() const @@ -507,11 +502,7 @@ namespace drawerbase { auto s = _m_caption(); if (s.empty()) return 0; - std::stringstream ss; - double value; - ss << to_utf8(s); - ss >> value; - return value; + return std::stod(s); } textbox& textbox::from(int n) @@ -599,6 +590,22 @@ namespace drawerbase { return (editor ? editor->line_height() : 0); } + void textbox::focus_behavior(text_focus_behavior behavior) + { + internal_scope_guard lock; + auto editor = get_drawer_trigger().editor(); + if (editor) + editor->focus_behavior(behavior); + } + + void textbox::select_behavior(bool move_to_end) + { + internal_scope_guard lock; + auto editor = get_drawer_trigger().editor(); + if (editor) + editor->select_behavior(move_to_end); + } + //Override _m_caption for caption() auto textbox::_m_caption() const throw() -> native_string_type { @@ -616,7 +623,7 @@ namespace drawerbase { auto editor = get_drawer_trigger().editor(); if (editor) { - editor->text(to_wstring(str)); + editor->text(to_wstring(str), false); API::update_window(this->handle()); } } diff --git a/source/gui/widgets/toolbar.cpp b/source/gui/widgets/toolbar.cpp index 6cf81234..94b5f888 100644 --- a/source/gui/widgets/toolbar.cpp +++ b/source/gui/widgets/toolbar.cpp @@ -308,7 +308,7 @@ namespace nana if (impl_->which != npos && container.at(impl_->which)->enable) { ::nana::arg_toolbar arg{ *widget_, impl_->which }; - widget_->events().leave.emit(arg); + widget_->events().leave.emit(arg, widget_->handle()); } impl_->which = which; @@ -317,12 +317,12 @@ namespace nana impl_->state = item_renderer::state_t::highlighted; refresh(graph); - API::lazy_refresh(); + API::dev::lazy_refresh(); if (impl_->state == item_renderer::state_t::highlighted) { ::nana::arg_toolbar arg{ *widget_, which }; - widget_->events().enter.emit(arg); + widget_->events().enter.emit(arg, widget_->handle()); } } @@ -341,12 +341,12 @@ namespace nana impl_->which = npos; refresh(graph); - API::lazy_refresh(); + API::dev::lazy_refresh(); if (which != npos && impl_->items.at(which)->enable) { ::nana::arg_toolbar arg{ *widget_, which }; - widget_->events().leave.emit(arg); + widget_->events().leave.emit(arg, widget_->handle()); } } impl_->tooltip.close(); @@ -359,7 +359,7 @@ namespace nana { impl_->state = item_renderer::state_t::selected; refresh(graph); - API::lazy_refresh(); + API::dev::lazy_refresh(); } } @@ -371,7 +371,7 @@ namespace nana if(impl_->which == which) { ::nana::arg_toolbar arg{ *widget_, which }; - widget_->events().selected.emit(arg); + widget_->events().selected.emit(arg, widget_->handle()); impl_->state = item_renderer::state_t::highlighted; } @@ -382,7 +382,7 @@ namespace nana } refresh(graph); - API::lazy_refresh(); + API::dev::lazy_refresh(); } } diff --git a/source/gui/widgets/treebox.cpp b/source/gui/widgets/treebox.cpp index 73913ba3..b0895943 100644 --- a/source/gui/widgets/treebox.cpp +++ b/source/gui/widgets/treebox.cpp @@ -496,7 +496,7 @@ namespace nana { data.stop_drawing = true; item_proxy iprx(data.trigger_ptr, node); - data.widget_ptr->events().checked.emit(::nana::arg_treebox{ *data.widget_ptr, iprx, (checkstate::unchecked != cs) }); + data.widget_ptr->events().checked.emit(::nana::arg_treebox{ *data.widget_ptr, iprx, (checkstate::unchecked != cs) }, data.widget_ptr->handle()); data.stop_drawing = false; } return true; @@ -512,14 +512,14 @@ namespace nana if (node_state.selected) { item_proxy iprx(data.trigger_ptr, node_state.selected); - data.widget_ptr->events().selected.emit(::nana::arg_treebox{ *data.widget_ptr, iprx, false }); + data.widget_ptr->events().selected.emit(::nana::arg_treebox{ *data.widget_ptr, iprx, false }, data.widget_ptr->handle()); } node_state.selected = node; if (node) { item_proxy iprx(data.trigger_ptr, node_state.selected); - data.widget_ptr->events().selected.emit(::nana::arg_treebox{ *data.widget_ptr, iprx, true }); + data.widget_ptr->events().selected.emit(::nana::arg_treebox{ *data.widget_ptr, iprx, true }, data.widget_ptr->handle()); } data.stop_drawing = false; return true; @@ -544,7 +544,7 @@ namespace nana data.stop_drawing = true; //attr.ext_event.expand(data.widget_ptr->handle(), item_proxy(data.trigger_ptr, node), value); item_proxy iprx(data.trigger_ptr, node); - data.widget_ptr->events().expanded.emit(::nana::arg_treebox{ *data.widget_ptr, iprx, value }); + data.widget_ptr->events().expanded.emit(::nana::arg_treebox{ *data.widget_ptr, iprx, value }, data.widget_ptr->handle()); data.stop_drawing = false; } return true; @@ -682,7 +682,7 @@ namespace nana if (node_state.pointed) { item_proxy iprx(data.trigger_ptr, node_state.pointed); - data.widget_ptr->events().hovered.emit(::nana::arg_treebox{ *data.widget_ptr, iprx, false }); + data.widget_ptr->events().hovered.emit(::nana::arg_treebox{ *data.widget_ptr, iprx, false }, data.widget_ptr->handle()); if (nl.node() != node_state.pointed) close_tooltip_window(); @@ -691,7 +691,7 @@ namespace nana node_state.pointed = nl.node(); item_proxy iprx(data.trigger_ptr, node_state.pointed); - data.widget_ptr->events().hovered.emit(::nana::arg_treebox{ *data.widget_ptr, iprx, true }); + data.widget_ptr->events().hovered.emit(::nana::arg_treebox{ *data.widget_ptr, iprx, true }, data.widget_ptr->handle()); redraw = (node_state.comp_pointed != component::end); @@ -709,7 +709,7 @@ namespace nana redraw = true; node_state.comp_pointed = component::end; item_proxy iprx(data.trigger_ptr, node_state.pointed); - data.widget_ptr->events().hovered.emit(::nana::arg_treebox{ *data.widget_ptr, iprx, false }); + data.widget_ptr->events().hovered.emit(::nana::arg_treebox{ *data.widget_ptr, iprx, false }, data.widget_ptr->handle()); close_tooltip_window(); node_state.pointed = nullptr; @@ -1270,12 +1270,12 @@ namespace nana if(size.width > attr.area.width || size.height > attr.area.height) { nana::size fit_size; - nana::fit_zoom(size, attr.area, fit_size); + nana::fit_zoom(size, attr.area.dimension(), fit_size); attr.area.x += (attr.area.width - fit_size.width) / 2; attr.area.y += (attr.area.height - fit_size.height) / 2; - attr.area = fit_size; - img->stretch(::nana::rectangle{ size }, graph, attr.area); + attr.area.dimension(fit_size); + img->stretch(rectangle{ size }, graph, attr.area); } else img->paste(graph, point{ attr.area.x + static_cast(attr.area.width - size.width) / 2, attr.area.y + static_cast(attr.area.height - size.height) / 2 }); @@ -1442,8 +1442,9 @@ namespace nana 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)) + 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; @@ -1879,7 +1880,7 @@ namespace nana impl_->node_state.event_node = nl.node(); impl_->set_expanded(impl_->node_state.event_node, !impl_->node_state.event_node->value.second.expanded); impl_->draw(true); - API::lazy_refresh(); + API::dev::lazy_refresh(); } } @@ -1927,7 +1928,7 @@ namespace nana if(has_redraw) { impl_->draw(true); - API::lazy_refresh(); + API::dev::lazy_refresh(); } } @@ -1960,7 +1961,7 @@ namespace nana return; //Do not refresh impl_->draw(true); - API::lazy_refresh(); + API::dev::lazy_refresh(); } void trigger::mouse_move(graph_reference, const arg_mouse& arg) @@ -1968,7 +1969,7 @@ namespace nana if(impl_->track_mouse(arg.pos.x, arg.pos.y)) { impl_->draw(false); - API::lazy_refresh(); + API::dev::lazy_refresh(); } } @@ -1986,7 +1987,7 @@ namespace nana impl_->track_mouse(arg.pos.x, arg.pos.y); impl_->draw(false); - API::lazy_refresh(); + API::dev::lazy_refresh(); } } @@ -1995,17 +1996,17 @@ namespace nana if (impl_->node_state.pointed && (!impl_->node_state.tooltip)) { item_proxy iprx(impl_->data.trigger_ptr, impl_->node_state.pointed); - impl_->data.widget_ptr->events().hovered.emit(::nana::arg_treebox{ *impl_->data.widget_ptr, iprx, false }); + impl_->data.widget_ptr->events().hovered.emit(::nana::arg_treebox{ *impl_->data.widget_ptr, iprx, false }, impl_->data.widget_ptr->handle()); impl_->node_state.pointed = nullptr; impl_->draw(false); - API::lazy_refresh(); + API::dev::lazy_refresh(); } } void trigger::resized(graph_reference, const arg_resized&) { impl_->draw(false); - API::lazy_refresh(); + API::dev::lazy_refresh(); impl_->show_scroll(); if(!impl_->shape.scroll.empty()) { @@ -2120,7 +2121,7 @@ namespace nana if(redraw) { impl_->draw(scroll); - API::lazy_refresh(); + API::dev::lazy_refresh(); } } @@ -2150,7 +2151,7 @@ namespace nana if (do_refresh) { impl_->draw(do_refresh & 1); - API::lazy_refresh(); + API::dev::lazy_refresh(); } } //end class trigger diff --git a/source/gui/widgets/widget.cpp b/source/gui/widgets/widget.cpp index 5982ea71..ac50687b 100644 --- a/source/gui/widgets/widget.cpp +++ b/source/gui/widgets/widget.cpp @@ -1,6 +1,6 @@ /* * The fundamental widget class implementation - * Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -183,6 +183,16 @@ namespace nana _m_size(sz); } + void widget::set_capture(bool ignore_children) + { + API::set_capture(*this, ignore_children); + } + + void widget::release_capture() + { + API::release_capture(*this); + } + nana::point widget::pos() const { return API::window_position(handle()); @@ -370,6 +380,24 @@ namespace nana { return std::unique_ptr(new widget::inner_widget_notifier(*wdg)); } + + //class widget_base + widget_base::~widget_base() + { + if (handle_) + API::close_window(handle_); + } + + window widget_base::handle() const + { + return handle_; + } + + void widget_base::_m_notify_destroy() + { + handle_ = nullptr; + } + //end class widget_base } }//end namespace nana diff --git a/source/gui/wvl.cpp b/source/gui/wvl.cpp index cedca916..33f027f3 100644 --- a/source/gui/wvl.cpp +++ b/source/gui/wvl.cpp @@ -13,6 +13,11 @@ #include #include +#include +#include +#include + +//#define NANA_AUTOMATIC_GUI_TESTING namespace nana { namespace detail @@ -23,8 +28,67 @@ namespace nana } } - void exec() + void click(widget& w) + { + std::cout << "Automatically clicking widget "<f // = {} ///< emit events to mimics user actions and may asert results + ) + { + #ifdef NANA_AUTOMATIC_GUI_TESTING + + std::cout << "Will wait " << wait << " sec...\n"; + + std::thread t([wait, &f, wait_end]() + { + Wait( wait ); + std::cout << "running... \n" ; + if (f) + { + f(); + std::cout << "\nCongratulations, this was not trivial !" << std::endl; + }else + { + std::cout << "\nJust a trivial test." << std::endl; + } + std::cout << "Done... \n"; + std::cout << "Now again "; + Wait(wait_end); + std::cout << "Done... Now API::exit all ...\n"; + API::exit_all(); + }); + + pump(); + if (t.joinable()) + t.join(); + + #else + static_cast(wait); + static_cast(wait_end); + static_cast(f); //to eliminte unused parameter compiler warning. + + pump(); + #endif + } }//end namespace nana diff --git a/source/internationalization.cpp b/source/internationalization.cpp index 1210826e..02f6c72f 100644 --- a/source/internationalization.cpp +++ b/source/internationalization.cpp @@ -10,6 +10,8 @@ * @file: nana/internationalization.cpp */ +#include + #include #include #include @@ -269,7 +271,7 @@ namespace nana if (i == mgr.table.end()) { auto result = mgr.table.emplace(wd, std::move(eval)); - result.first->second.destroy = nana::API::events(wd).destroy([wd]{ + result.first->second.destroy = nana::API::events(wd).destroy.connect([wd](const arg_destroy&){ auto & eval_mgr = get_eval_manager(); std::lock_guard lockgd(eval_mgr.mutex); @@ -513,3 +515,5 @@ namespace nana } //end class i18n_eval } + +#include diff --git a/source/paint/detail/image_bmp.hpp b/source/paint/detail/image_bmp.hpp index da86c0a3..23d7907e 100644 --- a/source/paint/detail/image_bmp.hpp +++ b/source/paint/detail/image_bmp.hpp @@ -13,8 +13,8 @@ #ifndef NANA_PAINT_DETAIL_IMAGE_BMP_HPP #define NANA_PAINT_DETAIL_IMAGE_BMP_HPP -#include "image_pixbuf.hpp" #include +#include "image_pixbuf.hpp" namespace nana{ namespace paint { @@ -309,7 +309,7 @@ namespace nana{ namespace paint return true; } - bool open(const nana::experimental::filesystem::path& filename) override + bool open(const std::experimental::filesystem::path& filename) override { std::ifstream ifs(filename.string(), std::ios::binary); if(ifs) diff --git a/source/paint/detail/image_ico.hpp b/source/paint/detail/image_ico.hpp index debabac5..44b49191 100644 --- a/source/paint/detail/image_ico.hpp +++ b/source/paint/detail/image_ico.hpp @@ -1,8 +1,8 @@ #ifndef NANA_PAINT_DETAIL_IMAGE_ICO_HPP #define NANA_PAINT_DETAIL_IMAGE_ICO_HPP +#include #include -#include namespace nana{ namespace paint { @@ -24,7 +24,7 @@ namespace nana{ namespace paint image_ico(bool is_ico); - bool open(const ::nana::experimental::filesystem::path& filename) override; + bool open(const std::experimental::filesystem::path& filename) override; bool open(const void* data, std::size_t bytes) override; bool alpha_channel() const override; bool empty() const override; diff --git a/source/paint/detail/image_jpeg.hpp b/source/paint/detail/image_jpeg.hpp index 2ae4b953..1f19373b 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 experimental::filesystem::path& jpeg_file) override + bool open(const std::experimental::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 9e01e8c6..30ba18e5 100644 --- a/source/paint/detail/image_png.hpp +++ b/source/paint/detail/image_png.hpp @@ -121,7 +121,7 @@ namespace nana delete[] row_ptrs; } public: - bool open(const experimental::filesystem::path& png_file) override + bool open(const std::experimental::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/detail/image_process_provider.cpp b/source/paint/detail/image_process_provider.cpp index 1c0c61ab..414d68dc 100644 --- a/source/paint/detail/image_process_provider.cpp +++ b/source/paint/detail/image_process_provider.cpp @@ -1,3 +1,4 @@ +#include #include #include diff --git a/source/paint/graphics.cpp b/source/paint/graphics.cpp index 07153126..15a29c52 100644 --- a/source/paint/graphics.cpp +++ b/source/paint/graphics.cpp @@ -867,7 +867,7 @@ namespace paint ::DeleteObject(hBmp); ::DeleteDC(hdcMem); #elif defined(NANA_X11) - + static_cast(file_utf8); //eliminate unused parameter compil warning. #endif } } @@ -1112,6 +1112,29 @@ namespace paint line_to({ r.x, r.y }, left_clr); } + void graphics::frame_rectangle(const ::nana::rectangle& r, const color& clr, unsigned gap) + { + palette(false, clr); + + if (r.width > gap * 2) + { + point left{ r.x + static_cast(gap), r.y }, right{ r.right() - static_cast(gap) - 1, r.y }; + line(left, right); + + left.y = right.y = r.bottom() - 1; + line(left, right); + } + + if (r.height > gap * 2) + { + point top{ r.x, r.y + static_cast(gap) }, bottom{ r.x, r.bottom() - static_cast(gap) - 1 }; + line(top, bottom); + + top.x = bottom.x = r.right() - 1; + line(top, bottom); + } + } + void graphics::gradual_rectangle(const ::nana::rectangle& rct, const ::nana::color& from, const ::nana::color& to, bool vertical) { #if defined(NANA_WINDOWS) @@ -1201,6 +1224,10 @@ namespace paint if(solid) rectangle(::nana::rectangle(r).pare_off(1), true, solid_clr); } + + //eliminate unused parameter compiler warning. + static_cast(radius_x); + static_cast(radius_y); #endif } } diff --git a/source/paint/image.cpp b/source/paint/image.cpp index f14de479..b654ca6a 100644 --- a/source/paint/image.cpp +++ b/source/paint/image.cpp @@ -11,7 +11,7 @@ * @contributors: * nabijaczleweli(pr#106) */ - +#include #include #include #include @@ -21,7 +21,7 @@ #include #include -#include +#include #if defined(NANA_ENABLE_JPEG) #include "detail/image_jpeg.hpp" @@ -34,16 +34,35 @@ #include "detail/image_bmp.hpp" #include "detail/image_ico.hpp" +#include "image_accessor.hpp" + +namespace fs = std::experimental::filesystem; + namespace nana { namespace paint { +#if defined(NANA_WINDOWS) + HICON image_accessor::icon(const nana::paint::image& img) + { + auto ico = dynamic_cast(img.image_ptr_.get()); + if (ico && ico->ptr()) + return *(ico->ptr()); + return nullptr; + } +#else + int image_accessor::icon(const image&) + { + return 0; + } +#endif + namespace detail { //class image_ico image_ico::image_ico(bool is_ico): is_ico_(is_ico){} - bool image_ico::open(const nana::experimental::filesystem::path& file) + bool image_ico::open(const fs::path& file) { close(); #if defined(NANA_WINDOWS) @@ -73,7 +92,8 @@ namespace paint return true; } #else - if(is_ico_){} //kill the unused compiler warning in Linux. + static_cast(is_ico_); //eliminate the unused compiler warning in Linux. + static_cast(file); //to eliminate the unused parameter compiler warning. #endif return false; } @@ -98,7 +118,9 @@ namespace paint } } #else - if(is_ico_){} //kill the unused compiler warning in Linux. + static_cast(is_ico_); //kill the unused compiler warning in Linux. + static_cast(data); //to eliminate unused parameter compiler warning. + static_cast(bytes); #endif return false; } @@ -129,6 +151,9 @@ namespace paint { #if defined(NANA_WINDOWS) ::DrawIconEx(graph.handle()->context, p_dst.x, p_dst.y, *ptr_, src_r.width, src_r.height, 0, 0, DI_NORMAL); +#else + static_cast(src_r); //eliminate unused parameter compiler warning. + static_cast(p_dst); #endif } } @@ -139,6 +164,8 @@ namespace paint { #if defined(NANA_WINDOWS) ::DrawIconEx(graph.handle()->context, r.x, r.y, *ptr_, r.width, r.height, 0, 0, DI_NORMAL); +#else + static_cast(r); //eliminate unused parameter compiler warning. #endif } } @@ -206,7 +233,7 @@ namespace paint return *this; } - std::shared_ptr create_image(const ::nana::experimental::filesystem::path & p) + std::shared_ptr create_image(const fs::path & p) { std::shared_ptr ptr; @@ -289,14 +316,14 @@ namespace paint bool image::open(const ::std::string& file) { - ::nana::experimental::filesystem::path path(file); + fs::path path(file); image_ptr_ = create_image(path); return (image_ptr_ ? image_ptr_->open(path) : false); } bool image::open(const std::wstring& file) { - ::nana::experimental::filesystem::path path(file); + fs::path path(file); image_ptr_ = create_image(path); return (image_ptr_ ? image_ptr_->open(path) : false); } diff --git a/source/paint/image_accessor.hpp b/source/paint/image_accessor.hpp new file mode 100644 index 00000000..73060e7e --- /dev/null +++ b/source/paint/image_accessor.hpp @@ -0,0 +1,30 @@ +/* +* Paint Image Accessor +* Nana C++ Library(http://www.nanapro.org) +* Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) +* +* Distributed under the Boost Software License, Version 1.0. +* (See accompanying file LICENSE_1_0.txt or copy at +* http://www.boost.org/LICENSE_1_0.txt) +* +* @file nana/paint/image_accessor.hpp +* @brief A declaration of class image_accessor. It is used to access image private data, internal use. +*/ +#ifndef NANA_PAINT_IMAGE_ACCESS_HEADER_INCLUDED +#define NANA_PAINT_IMAGE_ACCESS_HEADER_INCLUDED +namespace nana +{ + namespace paint + { + class image_accessor + { + public: +#if defined(NANA_WINDOWS) + static HICON icon(const image&); +#else + static int icon(const image&); +#endif + }; + } +} +#endif \ No newline at end of file diff --git a/source/paint/image_process_selector.cpp b/source/paint/image_process_selector.cpp index f95ab20e..9dc76e23 100644 --- a/source/paint/image_process_selector.cpp +++ b/source/paint/image_process_selector.cpp @@ -1,3 +1,4 @@ +#include #include namespace nana diff --git a/source/paint/pixel_buffer.cpp b/source/paint/pixel_buffer.cpp index c3330c8d..f29c4068 100644 --- a/source/paint/pixel_buffer.cpp +++ b/source/paint/pixel_buffer.cpp @@ -1,7 +1,7 @@ /* * Pixel Buffer Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2014 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -19,6 +19,7 @@ #include #include +#include namespace nana{ namespace paint { @@ -47,11 +48,37 @@ namespace nana{ namespace paint struct pixel_buffer::pixel_buffer_storage : private nana::noncopyable { + pixel_buffer_storage(const pixel_buffer_storage& other) = delete; + pixel_buffer_storage& operator=(const pixel_buffer_storage&) = delete; + + bool _m_alloc() + { + if (pixel_size.empty()) + return false; + + std::unique_ptr pxbuf{ new pixel_color_t[pixel_size.width * pixel_size.height] }; +#if defined(NANA_X11) + auto & spec = nana::detail::platform_spec::instance(); + x11.image = ::XCreateImage(spec.open_display(), spec.screen_visual(), 32, ZPixmap, 0, reinterpret_cast(pxbuf.get()), pixel_size.width, pixel_size.height, 32, 0); + x11.attached = false; + if (!x11.image) + throw std::runtime_error("Nana.pixel_buffer: XCreateImage failed"); + + if (static_cast(bytes_per_line) != x11.image->bytes_per_line) + { + x11.image->data = nullptr; + XDestroyImage(x11.image); + throw std::runtime_error("Nana.pixel_buffer: Invalid pixel buffer context."); + } +#endif + raw_pixel_buffer = pxbuf.release(); + return true; + } public: const drawable_type drawable; //Attached handle const nana::rectangle valid_r; const nana::size pixel_size; - pixel_color_t * raw_pixel_buffer; + pixel_color_t * raw_pixel_buffer{ nullptr }; const std::size_t bytes_per_line; bool alpha_channel{false}; #if defined(NANA_X11) @@ -90,33 +117,15 @@ namespace nana{ namespace paint : drawable(nullptr), valid_r(0, 0, static_cast(width), static_cast(height)), pixel_size(static_cast(width), static_cast(height)), - raw_pixel_buffer(new pixel_color_t[width * height]), bytes_per_line(width * sizeof(pixel_color_t)) { -#if defined(NANA_X11) - nana::detail::platform_spec & spec = nana::detail::platform_spec::instance(); - x11.image = ::XCreateImage(spec.open_display(), spec.screen_visual(), 32, ZPixmap, 0, reinterpret_cast(raw_pixel_buffer), width, height, 32, 0); - x11.attached = false; - if(nullptr == x11.image) - { - delete [] raw_pixel_buffer; - throw std::runtime_error("Nana.pixel_buffer: XCreateImage failed"); - } - - if(static_cast(bytes_per_line) != x11.image->bytes_per_line) - { - x11.image->data = nullptr; - XDestroyImage(x11.image); - delete [] raw_pixel_buffer; - throw std::runtime_error("Nana.pixel_buffer: Invalid pixel buffer context."); - } -#endif + _m_alloc(); } pixel_buffer_storage(drawable_type drawable, const nana::rectangle& want_r) : drawable(drawable), valid_r(valid_rectangle(paint::detail::drawable_size(drawable), want_r)), - pixel_size(valid_r), + pixel_size(valid_r.dimension()), #if defined(NANA_WINDOWS) raw_pixel_buffer(reinterpret_cast(reinterpret_cast(drawable->pixbuf_ptr + valid_r.x) + drawable->bytes_per_line * valid_r.y)), bytes_per_line(drawable->bytes_per_line) @@ -362,7 +371,7 @@ namespace nana{ namespace paint XDestroyImage(img); } #endif - }; + };//end struct pixel_buffer_storage pixel_buffer::pixel_buffer(drawable_type drawable, const nana::rectangle& want_rectangle) { @@ -977,7 +986,7 @@ namespace nana{ namespace paint } } - void pixel_buffer::gradual_rectangle(const ::nana::rectangle& draw_rct, const ::nana::color& from, const ::nana::color& to, double fade_rate, bool vertical) + void pixel_buffer::gradual_rectangle(const ::nana::rectangle& draw_rct, const ::nana::color& from, const ::nana::color& to, double /*fade_rate*/, bool vertical) { auto sp = storage_.get(); if (nullptr == sp) return; @@ -1105,5 +1114,150 @@ namespace nana{ namespace paint if (overlap(r, ::nana::rectangle{ this->size() }, good_r)) (*(sp->img_pro.blur))->process(*this, good_r, radius); } + + + //x' = x*cos(angle) - y*sin(angle) + //y' = y*cos(angle) - x*sin(angle) + class calc_rotate + { + enum class angles + { + none, half_pi, pi, triangle_pi + }; + public: + calc_rotate(double angle, const basic_point& origin): + specific_{ _m_spec(angle) }, + sin_a_{ std::sin(angle * degree_) }, + cos_a_{ std::cos(angle * degree_) }, + origin_{ origin } + { + } + + basic_point from(const point& p) + { + switch (specific_) + { + case angles::half_pi: + return{ p.y - origin_.y, origin_.x - p.x}; + case angles::pi: + return{ origin_.x - p.x, origin_.y - p.y}; + case angles::triangle_pi: + return{ origin_.y - p.y, p.x - origin_.x}; + default: + break; + } + + return{ + (p.x - origin_.x) * cos_a_ + (p.y - origin_.y) * sin_a_, + (p.y - origin_.y) * cos_a_ - (p.x - origin_.x) * sin_a_ + }; + } + + basic_point to(const point& p) + { + switch (specific_) + { + case angles::half_pi: + return{ origin_.y - p.y, p.x - origin_.x }; + case angles::pi: + return{ origin_.x - p.x, origin_.y - p.y }; + case angles::triangle_pi: + return{ p.y - origin_.y, origin_.x - p.x }; + default: + break; + } + + return{ + (p.x - origin_.x) * cos_a_ - (p.y - origin_.y) * sin_a_, + (p.y - origin_.y) * cos_a_ + (p.x - origin_.x) * sin_a_ + }; + } + private: + static angles _m_spec(double angle) + { + if (90.0 == angle) + return angles::half_pi; + else if (180.0 == angle) + return angles::pi; + else if (270.0 == angle) + return angles::triangle_pi; + + return angles::none; + } + private: + const angles specific_; + const double degree_{ std::acos(-1) / 180 }; + const double sin_a_; + const double cos_a_; + const basic_point origin_; + }; + + pixel_buffer pixel_buffer::rotate(double angle, const color& extend_color) + { + auto sp = storage_.get(); + if (!sp) + return{}; + + if (angle <= 0 || 360 <= angle) + { + return{*this}; + } + + const basic_point origin{ (sp->pixel_size.width - 1) / 2.0, (sp->pixel_size.height - 1) / 2.0 }; + calc_rotate point_rotate{ angle, origin }; + + nana::size size_rotated{ sp->pixel_size }; + + if (90 == angle || 270 == angle) + { + size_rotated.shift(); + } + else + { + point pw, ph; + if (angle < 180) + { + ph.x = static_cast(sp->pixel_size.width); + } + else if (angle > 180) + { + pw.x = static_cast(sp->pixel_size.width); + } + + size_rotated.width = static_cast(std::abs(point_rotate.from(pw).x) * 2) + 1; + size_rotated.height = static_cast(std::abs(point_rotate.from(ph).y) * 2) + 1; + } + + pixel_buffer rotated_pxbuf{ size_rotated.width, size_rotated.height }; + + const basic_point rotated_origin{ (size_rotated.width - 1) / 2.0, (size_rotated.height - 1) / 2.0 }; + + for (int y = 0; y < static_cast(size_rotated.height); ++y) + { + auto buf = rotated_pxbuf.raw_ptr(y); + + basic_point dest{ -rotated_origin.x, y - rotated_origin.y }; + dest = dest + origin; + + const int right_point = static_cast(dest.x) + static_cast(size_rotated.width); + + for (point point_dest{ static_cast(dest.x), static_cast(dest.y) }; point_dest.x < right_point; ++point_dest.x) + { + auto point_source = point_rotate.to(point_dest) + origin; + + if (0 <= point_source.x && point_source.x <= static_cast(sp->pixel_size.width - 1) && 0 <= point_source.y && point_source.y <= static_cast(sp->pixel_size.height - 1)) + { + *buf = this->raw_ptr(static_cast(point_source.y))[static_cast(point_source.x)]; + } + else + *buf = extend_color.px_color(); + + ++buf; + } + } + + + return rotated_pxbuf; + } }//end namespace paint }//end namespace nana diff --git a/source/paint/text_renderer.cpp b/source/paint/text_renderer.cpp index 4acd0f36..825639c6 100644 --- a/source/paint/text_renderer.cpp +++ b/source/paint/text_renderer.cpp @@ -582,5 +582,97 @@ namespace nana } } //end class text_renderer + + //class aligner + + //Constructor + aligner::aligner(graph_reference graph, align text_align) + : aligner{ graph, text_align, text_align } + {} + + aligner::aligner(graph_reference graph, align text_align, align text_align_ex) : + graph_(graph), + text_align_(text_align), + text_align_ex_(text_align_ex) + {} + + // Draws a text with specified text alignment. + void aligner::draw(const std::string& text, point pos, unsigned width) + { + draw(to_wstring(text), pos, width); + } + + void aligner::draw(const std::wstring& text, point pos, unsigned width) + { + auto text_px = graph_.text_extent_size(text).width; + if (text_px <= width) + { + switch (text_align_) + { + case align::center: + pos.x += static_cast(width - text_px) / 2; + break; + case align::right: + pos.x += static_cast(width - text_px); + default: + break; + } + + graph_.bidi_string(pos, text.c_str(), text.size()); + return; + } + + const auto ellipsis = graph_.text_extent_size("...", 3).width; + + std::unique_ptr pixels(new unsigned[text.size()]); + graph_.glyph_pixels(text.c_str(), text.size(), pixels.get()); + + std::size_t substr_len = 0; + unsigned substr_px = 0; + + if (align::right == text_align_ex_) + { + auto end = pixels.get(); + auto p = end + text.size(); + do + { + --p; + if (substr_px + *p + ellipsis > width) + { + substr_len = p - pixels.get() + 1; + break; + } + substr_px += *p; + } while (p != end); + + pos.x += static_cast(width - ellipsis - substr_px); + + graph_.string(pos, "..."); + pos.x += ellipsis; + graph_.bidi_string(pos, text.c_str() + substr_len, text.size() - substr_len); + } + else + { + for (auto p = pixels.get(), end = pixels.get() + text.size(); p != end; ++p) + { + if (substr_px + *p + ellipsis > width) + { + substr_len = p - pixels.get(); + break; + } + substr_px += *p; + } + + if (align::center == text_align_ex_) + pos.x += (width - substr_px - ellipsis) / 2; + + graph_.bidi_string(pos, text.c_str(), substr_len); + + pos.x += substr_px; + graph_.string(pos, "..."); + } + } + + //end class string } } diff --git a/source/system/dataexch.cpp b/source/system/dataexch.cpp index 992a76d3..cb8ecb97 100644 --- a/source/system/dataexch.cpp +++ b/source/system/dataexch.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #if defined(NANA_WINDOWS) #include @@ -82,31 +83,35 @@ namespace nana{ namespace system{ // Bitmaps are huge, so to avoid unnegligible extra copy, this routine does not use private _m_set method. HGLOBAL h_gmem = ::GlobalAlloc(GHND | GMEM_SHARE, header_size + bitmap_bytes); void * gmem = ::GlobalLock(h_gmem); - if (gmem) { - char* p = (char*)gmem; - // Fix BITMAPINFOHEADER obtained from GetDIBits WinAPI - bmi.bmiHeader.biCompression = BI_RGB; - bmi.bmiHeader.biHeight = ::abs(bmi.bmiHeader.biHeight); - memcpy(p, &bmi, header_size); - p += header_size; - // many programs do not support bottom-up DIB, so reversing row order is needed. - for (int y=0; y(g); //eliminate unused parameter compiler warning. throw "not implemented yet."; return false; #endif diff --git a/source/system/platform.cpp b/source/system/platform.cpp index 042c5892..092c53e5 100644 --- a/source/system/platform.cpp +++ b/source/system/platform.cpp @@ -93,6 +93,7 @@ namespace system return (::GetAsyncKeyState(button) != 0); #elif defined(NANA_LINUX) || defined(NANA_MACOS) + static_cast(button); //eliminate unused parameter compiler warning. return false; #endif } diff --git a/source/system/shared_wrapper.cpp b/source/system/shared_wrapper.cpp index dce6d621..124f2822 100644 --- a/source/system/shared_wrapper.cpp +++ b/source/system/shared_wrapper.cpp @@ -54,7 +54,7 @@ namespace system ::FreeLibrary(reinterpret_cast(handle)); #endif } - }; //end struct shared_helper + } //end namespace shared_helper }//end namespace detail diff --git a/source/unicode_bidi.cpp b/source/unicode_bidi.cpp index 5ef940ec..f92bd5ee 100644 --- a/source/unicode_bidi.cpp +++ b/source/unicode_bidi.cpp @@ -584,7 +584,7 @@ namespace nana _m_resolve_weak_types(); _m_resolve_neutral_types(); _m_resolve_implicit_levels(); - _m_reordering_resolved_levels(str, reordered); + _m_reordering_resolved_levels(reordered); } unsigned unicode_bidi::_m_paragraph_level(const char_type * begin, const char_type * end) @@ -885,7 +885,7 @@ namespace nana } } - void unicode_bidi::_m_reordering_resolved_levels(const char_type * str, std::vector & reordered) + void unicode_bidi::_m_reordering_resolved_levels(std::vector & reordered) { reordered = levels_;