From ee093bea3d5136f6ce50c415b8d03329961ed60b Mon Sep 17 00:00:00 2001 From: Simone Gasparini Date: Sat, 30 Mar 2019 18:43:00 +0100 Subject: [PATCH] [geogram] Update to 1.6.11 --- CMakeLists.txt | 2 +- cmake/FindGeogram.cmake | 4 +- .../geogram/simple_raytrace/raytracing.h | 7 +- src/lib/geogram/NL/nl_cuda.c | 34 +- src/lib/geogram/basic/attributes.h | 31 + src/lib/geogram/basic/command_line_args.cpp | 23 +- src/lib/geogram/basic/process.h.new | 803 ---- src/lib/geogram/basic/process.h.old | 866 ---- src/lib/geogram/basic/thread_sync.h | 3 +- .../geogram/delaunay/periodic_delaunay_3d.cpp | 15 +- .../geogram/delaunay/periodic_delaunay_3d.h | 4 +- .../geogram/image/image_serializer_xpm.cpp | 233 +- src/lib/geogram/image/image_serializer_xpm.h | 28 +- src/lib/geogram/mesh/mesh.cpp | 18 +- src/lib/geogram/mesh/mesh.h | 11 +- src/lib/geogram/mesh/mesh_AABB.cpp | 14 +- src/lib/geogram/mesh/mesh_AABB.cpp.debug | 895 +++++ src/lib/geogram/mesh/mesh_AABB.h | 80 +- src/lib/geogram/mesh/mesh_AABB.h.debug | 656 ++++ src/lib/geogram/mesh/mesh_reorder.cpp | 4 +- src/lib/geogram/numerics/Predicates.psm | 1 + src/lib/geogram/numerics/predicates.cpp | 41 +- src/lib/geogram/numerics/predicates.h | 11 +- .../numerics/predicates/dot_compare_3d.h | 87 + .../numerics/predicates/dot_compare_3d.pck | 13 + src/lib/geogram/voronoi/RVD.cpp.new | 2651 ------------- src/lib/geogram/voronoi/RVD.cpp.old | 2651 ------------- src/lib/geogram/voronoi/calc.txt | 54 + src/lib/geogram/voronoi/convex_cell.cpp | 110 +- src/lib/geogram/voronoi/convex_cell.h | 89 +- src/lib/geogram_gfx/GLUP/GLUP.cpp | 52 +- src/lib/geogram_gfx/GLUP/GLUP.h | 21 - src/lib/geogram_gfx/GLUP/GLUP_context.cpp | 162 - src/lib/geogram_gfx/GLUP/GLUP_context.h | 22 - .../GLUP/GLUP_context_VanillaGL.cpp | 938 ----- .../geogram_gfx/GLUP/GLUP_context_VanillaGL.h | 415 -- src/lib/geogram_gfx/GLUP/GLUP_sphere.cpp | 2028 ---------- src/lib/geogram_gfx/ImGui_ext/file_dialog.cpp | 61 +- src/lib/geogram_gfx/ImGui_ext/icon_font.cpp | 1233 ++++++ src/lib/geogram_gfx/ImGui_ext/icon_font.h | 81 + src/lib/geogram_gfx/basic/GL.cpp | 51 - src/lib/geogram_gfx/basic/GL.h | 87 +- src/lib/geogram_gfx/basic/GLSL.cpp | 8 - .../glup_viewer/glup_viewer_gui_private.cpp | 39 +- src/lib/geogram_gfx/lua/lua_imgui.cpp | 1226 +----- src/lib/geogram_gfx/lua/lua_imgui.h | 2 +- src/lib/geogram_gfx/mesh/mesh_gfx.cpp | 6 - src/lib/geogram_gfx/mesh/mesh_gfx.h | 4 - .../third_party/ImGui/OLD/README.txt | 2 +- .../third_party/ImGui/OLD/imconfig.h | 4 + .../third_party/ImGui/OLD/imgui.cpp | 1898 +++++---- .../geogram_gfx/third_party/ImGui/OLD/imgui.h | 160 +- .../third_party/ImGui/OLD/imgui_demo.cpp | 3482 +++++++++-------- .../third_party/ImGui/OLD/imgui_draw.cpp | 125 +- .../third_party/ImGui/OLD/imgui_impl_glfw.cpp | 75 +- .../third_party/ImGui/OLD/imgui_impl_glfw.h | 18 +- .../ImGui/OLD/imgui_impl_opengl2.cpp | 233 -- .../ImGui/OLD/imgui_impl_opengl2.h | 45 - .../ImGui/OLD/imgui_impl_opengl3.cpp | 30 +- .../ImGui/OLD/imgui_impl_opengl3.h | 15 +- .../ImGui/OLD/imgui_impl_win32.cpp | 5 +- .../third_party/ImGui/OLD/imgui_impl_win32.h | 9 +- .../third_party/ImGui/OLD/imgui_internal.h | 56 +- .../third_party/ImGui/OLD/imgui_widgets.cpp | 476 ++- .../OLD/{stb_rect_pack.h => imstb_rectpack.h} | 0 .../OLD/{stb_textedit.h => imstb_textedit.h} | 0 .../OLD/{stb_truetype.h => imstb_truetype.h} | 0 .../geogram_gfx/third_party/ImGui/README.txt | 2 +- .../geogram_gfx/third_party/ImGui/imgui.cpp | 1825 ++++++--- src/lib/geogram_gfx/third_party/ImGui/imgui.h | 1064 ++--- .../third_party/ImGui/imgui_demo.cpp | 1653 +++++--- .../third_party/ImGui/imgui_draw.cpp | 673 ++-- .../third_party/ImGui/imgui_impl_glfw.cpp | 96 +- .../third_party/ImGui/imgui_impl_glfw.h | 17 +- .../third_party/ImGui/imgui_impl_opengl2.cpp | 232 -- .../third_party/ImGui/imgui_impl_opengl2.h | 51 - .../third_party/ImGui/imgui_impl_opengl3.cpp | 114 +- .../third_party/ImGui/imgui_impl_opengl3.h | 43 +- .../third_party/ImGui/imgui_impl_win32.cpp | 120 +- .../third_party/ImGui/imgui_impl_win32.h | 14 +- .../third_party/ImGui/imgui_internal.h | 510 ++- .../third_party/ImGui/imgui_widgets.cpp | 2426 +++++++++--- .../third_party/ImGui/imstb_rectpack.h | 15 +- .../third_party/ImGui/imstb_textedit.h | 26 +- .../third_party/ImGui/imstb_truetype.h | 95 +- .../ImGuiColorTextEdit/TextEditor.cpp | 28 +- .../imgui_lua_bindings/imgui_iterator.h | 649 +-- .../imgui_lua_bindings/imgui_iterator.h.000 | 1665 ++++++++ 88 files changed, 15143 insertions(+), 18683 deletions(-) mode change 100755 => 100644 cmake/FindGeogram.cmake delete mode 100644 src/lib/geogram/basic/process.h.new delete mode 100644 src/lib/geogram/basic/process.h.old create mode 100644 src/lib/geogram/mesh/mesh_AABB.cpp.debug create mode 100644 src/lib/geogram/mesh/mesh_AABB.h.debug create mode 100644 src/lib/geogram/numerics/predicates/dot_compare_3d.h create mode 100644 src/lib/geogram/numerics/predicates/dot_compare_3d.pck delete mode 100644 src/lib/geogram/voronoi/RVD.cpp.new delete mode 100644 src/lib/geogram/voronoi/RVD.cpp.old create mode 100644 src/lib/geogram/voronoi/calc.txt delete mode 100755 src/lib/geogram_gfx/GLUP/GLUP_context_VanillaGL.cpp delete mode 100755 src/lib/geogram_gfx/GLUP/GLUP_context_VanillaGL.h delete mode 100755 src/lib/geogram_gfx/GLUP/GLUP_sphere.cpp create mode 100644 src/lib/geogram_gfx/ImGui_ext/icon_font.cpp create mode 100755 src/lib/geogram_gfx/ImGui_ext/icon_font.h mode change 100755 => 100644 src/lib/geogram_gfx/lua/lua_imgui.h delete mode 100644 src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_opengl2.cpp delete mode 100644 src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_opengl2.h rename src/lib/geogram_gfx/third_party/ImGui/OLD/{stb_rect_pack.h => imstb_rectpack.h} (100%) rename src/lib/geogram_gfx/third_party/ImGui/OLD/{stb_textedit.h => imstb_textedit.h} (100%) rename src/lib/geogram_gfx/third_party/ImGui/OLD/{stb_truetype.h => imstb_truetype.h} (100%) delete mode 100644 src/lib/geogram_gfx/third_party/ImGui/imgui_impl_opengl2.cpp delete mode 100644 src/lib/geogram_gfx/third_party/ImGui/imgui_impl_opengl2.h create mode 100644 src/lib/geogram_gfx/third_party/imgui_lua_bindings/imgui_iterator.h.000 diff --git a/CMakeLists.txt b/CMakeLists.txt index 682db822..7e8cc030 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,7 +48,7 @@ include(cmake/geogram.cmake) set(VORPALINE_VERSION_MAJOR 1) set(VORPALINE_VERSION_MINOR 6) -set(VORPALINE_VERSION_PATCH 10) +set(VORPALINE_VERSION_PATCH 11) set(VORPALINE_VERSION ${VORPALINE_VERSION_MAJOR}.${VORPALINE_VERSION_MINOR}.${VORPALINE_VERSION_PATCH}) set(VORPALINE_INCLUDE_SUBPATH geogram${VORPALINE_VERSION_MAJOR}) diff --git a/cmake/FindGeogram.cmake b/cmake/FindGeogram.cmake old mode 100755 new mode 100644 index ab451d56..d1cc2844 --- a/cmake/FindGeogram.cmake +++ b/cmake/FindGeogram.cmake @@ -53,7 +53,7 @@ find_library (GEOGRAM_GFX_LIBRARY # system search path since it may be already installed # in the system find_library (GEOGRAM_GLFW3_LIBRARY - NAMES glfw3 glfw geogram_glfw3 + NAMES glfw3 glfw geogram_glfw3 glfw3dll glfwdll PATHS ${GEOGRAM_SEARCH_PATHS} ${GEOGRAM_SEARCH_PATHS_SYSTEM} PATH_SUFFIXES lib ) @@ -66,7 +66,7 @@ find_package_handle_standard_args( # Create an imported target for Geogram If (GEOGRAM_FOUND) - set(GEOGRAM_INSTALL_PREFIX ${GEOGRAM_INCLUDE_DIR}/..) + set(GEOGRAM_INSTALL_PREFIX ${GEOGRAM_INCLUDE_DIR}/../..) if (NOT TARGET Geogram::geogram) add_library (Geogram::geogram UNKNOWN IMPORTED) diff --git a/src/examples/geogram/simple_raytrace/raytracing.h b/src/examples/geogram/simple_raytrace/raytracing.h index 467048a9..fb5520e2 100644 --- a/src/examples/geogram/simple_raytrace/raytracing.h +++ b/src/examples/geogram/simple_raytrace/raytracing.h @@ -189,9 +189,8 @@ namespace GEO { Y_ = cross(Z_,X_); // Coordinate of the viewing plane along viewing vector - double zp = ( - double(image_height_) / 2.0) / tan(zoom * M_PI / 180.0 - ); + double zp = + (double(image_height_) / 2.0) / tan(zoom * M_PI / 180.0); // Center of the viewing plane center_ = position_+zp*Z_; @@ -323,7 +322,7 @@ namespace GEO { Intersection() : t(Numeric::max_float64()), object(nullptr), - K(0.0, 0.0, 0.3) { + K(0.1, 0.1, 0.1) { } vec3 position; /**< position of the intersection. */ vec3 normal; /**< normal to the object. */ diff --git a/src/lib/geogram/NL/nl_cuda.c b/src/lib/geogram/NL/nl_cuda.c index 1c104fe9..8287c591 100644 --- a/src/lib/geogram/NL/nl_cuda.c +++ b/src/lib/geogram/NL/nl_cuda.c @@ -142,6 +142,8 @@ enum cudaMemcpyKind { typedef int cudaError_t; +typedef cudaError_t (*FUNPTR_cudaDriverGetVersion)(int* version); +typedef cudaError_t (*FUNPTR_cudaRuntimeGetVersion)(int* version); typedef cudaError_t (*FUNPTR_cudaGetDeviceCount)(int* device_count); typedef cudaError_t (*FUNPTR_cudaGetDeviceProperties)( struct cudaDeviceProp *props, int device @@ -279,7 +281,7 @@ typedef cublasStatus_t (*FUNPTR_cublasDtpsv)( * \details Function pointers are stored into the * CUDAContext returned by the function CUDA(). * If a symbol is not found, returns NL_FALSE from the - * calling function. Here we use the functions prefixed + * calling function. Here we use the functions suffixed * by "_v2". */ #define find_cublas_func(name) \ @@ -301,8 +303,7 @@ typedef cublasStatus_t (*FUNPTR_cublasDtpsv)( * \details Function pointers are stored into the * CUDAContext returned by the function CUDA(). * If a symbol is not found, returns NL_FALSE from the - * calling function. Here we use the functions prefixed - * by "_v2". + * calling function. */ #define find_cublas_func_v1(name) \ if( \ @@ -441,6 +442,9 @@ typedef cusparseStatus_t (*FUNPTR_cusparseDhybmv)( */ typedef struct { NLdll DLL_cudart; + + FUNPTR_cudaDriverGetVersion cudaDriverGetVersion; + FUNPTR_cudaRuntimeGetVersion cudaRuntimeGetVersion; FUNPTR_cudaGetDeviceCount cudaGetDeviceCount; FUNPTR_cudaGetDeviceProperties cudaGetDeviceProperties; FUNPTR_cudaDeviceReset cudaDeviceReset; @@ -497,6 +501,8 @@ static CUDAContext* CUDA() { NLboolean nlExtensionIsInitialized_CUDA() { if( CUDA()->DLL_cudart == NULL || + CUDA()->cudaDriverGetVersion == NULL || + CUDA()->cudaRuntimeGetVersion == NULL || CUDA()->cudaGetDeviceCount == NULL || CUDA()->cudaGetDeviceProperties == NULL || CUDA()->cudaDeviceReset == NULL || @@ -614,9 +620,23 @@ static int getBestDeviceID() { int max_compute_perf = 0, max_perf_device = 0; int device_count = 0, best_SM_arch = 0; int compute_perf = 0; + int driver_ver = 0; + int runtime_ver = 0; struct cudaDeviceProp deviceProp; - CUDA()->cudaGetDeviceCount(&device_count); + int retval = CUDA()->cudaGetDeviceCount(&device_count); + if(retval == 35) { + nl_printf("Error: Driver/CUDA versions mismatch\n"); + retval = CUDA()->cudaDriverGetVersion(&driver_ver); + nl_printf("cudaDriverGetVersion() retval=%d\n",retval); + retval = CUDA()->cudaRuntimeGetVersion(&runtime_ver); + nl_printf("cudaRuntimeGetVersion() retval=%d\n",retval); + + nl_printf(" Driver version=%d\n",driver_ver); + nl_printf(" Runtime version=%d\n",driver_ver); + } + /* Find the best major SM Architecture GPU device */ + while (current_device < device_count) { CUDA()->cudaGetDeviceProperties(&deviceProp, current_device); /* If this GPU is not running on Compute Mode prohibited, @@ -667,7 +687,7 @@ static int getBestDeviceID() { nl_printf( "OpenNL CUDA: maximum device single-precision Gflops=%f\n", (double)(2*max_compute_perf)/(double)(1e6) - ); + ); */ return max_perf_device; @@ -706,6 +726,8 @@ NLboolean nlInitExtension_CUDA(void) { LIBPREFIX "cudart" LIBEXTENSION, flags ); + find_cuda_func(cudaDriverGetVersion); + find_cuda_func(cudaRuntimeGetVersion); find_cuda_func(cudaGetDeviceCount); find_cuda_func(cudaGetDeviceProperties); find_cuda_func(cudaDeviceReset); @@ -713,7 +735,7 @@ NLboolean nlInitExtension_CUDA(void) { find_cuda_func(cudaFree); find_cuda_func(cudaMemcpy); - CUDA()->devID = getBestDeviceID(); + CUDA()->devID = getBestDeviceID(); if(CUDA()->cudaGetDeviceProperties(&deviceProp, CUDA()->devID)) { nl_fprintf(stderr,"OpenNL CUDA: could not find a CUDA device\n"); diff --git a/src/lib/geogram/basic/attributes.h b/src/lib/geogram/basic/attributes.h index 74e2ced5..64a0566c 100644 --- a/src/lib/geogram/basic/attributes.h +++ b/src/lib/geogram/basic/attributes.h @@ -1277,6 +1277,37 @@ namespace GEO { } } + /** + * \brief Copies all the values from another attribute. + * \param[in] rhs the attribute to be copied. + * \details rhs needs to have the same size and dimension + * as this Attribute. + */ + void copy(const Attribute& rhs) { + geo_assert(rhs.size() == superclass::size()); + geo_assert(rhs.dimension() == superclass::dimension()); + for(index_t i=0; i -#include -#include -#include -#include - -/** - * \file geogram/basic/process.h - * \brief Function and classes for process manipulation - */ - -namespace GEO { - -// thread_local is supposed to be supported by c++0x, -// but some old MSVC compilers do not have it. -#if defined(GEO_COMPILER_MSVC) && !defined(thread_local) -# define thread_local __declspec(thread) -#endif - -// Older MAC OS X do not have thread_local -#ifdef GEO_OS_APPLE -# if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_9 -# define thread_local -# define GEO_NO_THREAD_LOCAL -# endif -#endif - - /** - * \brief Platform-independent base class for running threads. - * \details - * A Thread object manages one thread of control within the program. - * Thread%s begin executing with run(). Operational threads can be created - * by creating a derived class and reimplement function run(). - * - * Thread%s are reference-counted objects. Their allocation and - * destruction can be automatically managed with Thread_var. - */ - class GEOGRAM_API Thread : public Counted { - public: - - /** - * \brief Thread constructor. - */ - Thread() : id_(0) { - } - - /** - * \brief Starts the thread execution. - */ - virtual void run() = 0; - - /** - * \brief Gets the identifier of this thread. - * \return the identifier of the thread, i.e. - * an unsigned integer in the range [0, N-1] - * where N denotes the number of currently - * running threads. - */ - index_t id() const { - return id_; - } - - /** - * \brief Gets the current thread. - * \return A pointer to the instance of the - * currently running thread. - */ - static Thread* current(); - - protected: - /** Thread destructor */ - virtual ~Thread(); - - - private: - /** - * \brief Sets the identifier of this thread. - * \details This function is meant to be called - * by the thread manager for each created thread. - * \param[in] id_in the identifier of this thread. - */ - void set_id(index_t id_in) { - id_ = id_in; - } - - /** - * \brief Specifies the current instance, used by current(). - * \details Stores the specified thread in the thread-local-storage - * static variable so that current() can retrieve it. - * Should be called by the ThreadManager right before launching - * the threads. - * \param[in] thread a pointer to the thread currently executed - */ - static void set_current(Thread* thread); - - index_t id_; - - // ThreadManager needs to access set_current() and - // set_id(). - friend class ThreadManager; - }; - - /** Smart pointer that contains a Thread object */ - typedef SmartPointer Thread_var; - - /** - * \brief Collection of Thread%s - * \details ThreadGroup is a std::vector of Thread_var it provides the - * same operations for adding, removing or accessing thread elements. - * ThreadGroup takes ownership of Thread elements when they are added to - * the group, so there's is no need to delete Threads when the group is - * deleted. - */ - typedef std::vector ThreadGroup; - - /** - * \brief Typed collection of Thread%s. - * \details - * TypedThreadGroup is a ThreadGroup that provides a typed accessor with - * operator[](). - * \tparam THREAD the type of Thread%s in the collection - */ - template - class TypedThreadGroup : public ThreadGroup { - public: - /** - * \brief Creates an empty group of Thread%s - * \details Thread elements can be added with the std::vector - * operation push_back() - */ - TypedThreadGroup() { - } - - /** - * \brief Gets a thread element by index - * \param[in] i index of the element - * \return a pointer to the \p THREAD at position \p i in the - * thread group - */ - THREAD* operator[] (index_t i) { - geo_debug_assert(i < size()); - Thread* result = ThreadGroup::operator[] (i); - return static_cast(result); - } - }; - - /** - * \brief Platform-independent base class for running concurrent threads. - * \details - * The ThreadManager manager provides a platform-independent abstract - * interface for running concurrent Threads and managing critical - * sections. - * - * The ThreadManager is derived in multiple platform-specific or - * technology-specific implementations. - * - * Platform-specific implementations: - * - POSIX Thread manager (Unix) - * - Windows Threads manager (Windows) - * - Windows ThreadPool manager (Windows) - * - * Technology-specific implementations: - * - OpenMP-based manager - * - * Which ThreadManager to use is determined at runtime by - * Process::initialize() according to the current platform or the current - * available technology. - * - * \note For internal use only. - * \see Process::set_thread_manager() - */ - class GEOGRAM_API ThreadManager : public Counted { - public: - /** - * \brief Runs a group of Thread%s. - * \details - * This start the execution of the threads - * contained in vector \p threads. - * - * If the threads cannot be executed in a concurrent environment - * (multi-threading is disabled or the number of maximum threads is 1), - * then the threads are executed sequentially. Otherwise the function - * run_concurrent_threads() is called to execute the threads - * concurrently. The execution terminates when the last thread - * terminates. - * - * \param[in] threads the vector of threads to be executed. - * \see maximum_concurrent_threads() - * \see run_concurrent_threads() - * \see Process::max_threads() - */ - virtual void run_threads(ThreadGroup& threads); - - /** - * \brief Gets the maximum number of possible concurrent threads - * \return The maximum number of possible concurrent threads allowed - * by this manager. It depends on the physical number of cores - * (including hyper-threading or not) and the technology implemented - * by this manager. - * \see Process::number_of_cores() - */ - virtual index_t maximum_concurrent_threads() = 0; - - /** - * \brief Enters a critical section - * \details - * One thread at a time can enter the critical section, all the other - * threads that call this function are blocked until the blocking - * thread leaves the critical section. - * \see leave_critical_section() - */ - virtual void enter_critical_section() = 0; - - /** - * \brief Leaves a critical section - * \details When a blocking thread leaves a critical section, this - * makes the critical section available for a waiting thread. - * \see enter_critical_section() - */ - virtual void leave_critical_section() = 0; - - protected: - /** - * \brief Runs a group of Thread%s concurrently. - * \details This start the concurrent execution of the threads - * contained in vector \p threads, using the given number of threads - * \p max_threads. The execution terminates when the last thread - * terminates. - * \param[in] threads the vector of threads to be executed. - * \param[in] max_threads maximum number of threads allowed for this - * execution. It is always greater than one - */ - virtual void run_concurrent_threads( - ThreadGroup& threads, index_t max_threads - ) = 0; - - - /** - * \brief Sets the id of a thread. - * \details This function is called right before starting - * the threads. Each thread will have an id in [0, N-1] - * where N denotes the number of running threads. - * \param[in] thread the thread - * \param[in] id the id - */ - static void set_thread_id(Thread* thread, index_t id) { - thread->set_id(id); - } - - /** - * \brief Specifies the current instance, used by current(). - * \details Stores the specified thread in the thread-local-storage - * static variable so that current() can retrieve it. - * Should be called by the ThreadManager right before launching - * the threads. - * \param[in] thread a pointer to the thread currently executed - */ - static void set_current_thread(Thread* thread) { - Thread::set_current(thread); - } - - /** ThreadManager destructor */ - virtual ~ThreadManager(); - }; - - /** Smart pointer that contains a ThreadManager object */ - typedef SmartPointer ThreadManager_var; - - /** - * \brief Single thread ThreadManager - * \details MonoThreadingThreadManager implements a ThreadManager for - * single thread environments. - */ - class GEOGRAM_API MonoThreadingThreadManager : public ThreadManager { - public: - /** - * \copydoc ThreadManager::maximum_concurrent_threads() - * \note This implementation always returns 1. - */ - virtual index_t maximum_concurrent_threads(); - - /** - * \copydoc ThreadManager::enter_critical_section() - * \note This implementation does actually nothing - */ - virtual void enter_critical_section(); - - /** - * \copydoc ThreadManager::leave_critical_section() - * \note This implementation does actually nothing - */ - virtual void leave_critical_section(); - - protected: - /** MonoThreadingThreadManager destructor */ - virtual ~MonoThreadingThreadManager(); - - /** - * \copydoc ThreadManager::run_concurrent_threads() - * \note This implementation always executes threads sequentially. - */ - virtual void run_concurrent_threads( - ThreadGroup& threads, index_t max_threads - ); - }; - - /** - * \brief Abstraction layer for process management and multi-threading. - */ - namespace Process { - - /** - * \brief Initializes GeogramLib - * \param[in] flags the flags passed to GEO::initialize() - * \details This function must be called once before using - * any functionality of GeogramLib. - */ - void GEOGRAM_API initialize(int flags); - - /** - * \brief Terminates GeogramLib - * \details This function is called automatically when the program - * exits, so it should never be called directly. - */ - void GEOGRAM_API terminate(); - - /** - * \brief Displays statistics about the current process - * \details Displays the maximum used amount of memory. - */ - void GEOGRAM_API show_stats(); - - /** - * \brief Terminates the current process. - */ - void GEOGRAM_API brute_force_kill(); - - /** - * \brief Returns the maximum number of threads that can be running - * simultaneously. - * \retval The number of cores if multi-threading is supported - * \retval 1 otherwise. - */ - index_t GEOGRAM_API maximum_concurrent_threads(); - - /** - * \brief Runs a set of threads simultaneously - * \details Launches the execution of the threads contained in the - * vector \p threads and waits for the completion of all of them. - */ - void GEOGRAM_API run_threads(ThreadGroup& threads); - - /** - * \brief Enters a critical section - * \details One thread at a time can enter the critical section, - * all the other threads that call this function are blocked until the - * blocking thread leaves the critical section - * \see ThreadManager::enter_critical_section() - * \see leave_critical_section() - */ - void GEOGRAM_API enter_critical_section(); - - /** - * \brief Leaves a critical section - * \details When a blocking thread leaves a critical section, this - * makes the critical section available for a waiting thread. - * \see ThreadManager::leave_critical_section() - * \see enter_critical_section() - */ - void GEOGRAM_API leave_critical_section(); - - /** - * \brief Gets the number of available cores - * \return The number of available cores including the "virtual ones" if - * hyper-threading is activated. - */ - index_t GEOGRAM_API number_of_cores(); - - /** - * \brief Sets the thread manager (internal use). - * \details This sets the ThreadManager to use for concurrent thread - * execution. This function is called internally by - * Process::initialize() and should not be called explicitly. - * \note For internal use only - */ - void GEOGRAM_API set_thread_manager(ThreadManager* thread_manager); - - /** - * \brief Checks whether threads are running. - * \retval true if concurrent threads are currently running as an - * effect to Process::run_threads(). - * \retval false otherwise. - * \see Process::run_threads() - */ - bool GEOGRAM_API is_running_threads(); - - /** - * \brief Enables/disables floating point exceptions - * \details If FPEs are enabled, then floating point exceptions - * raise a SIGFPE signal, otherwise they generate NaNs. FPEs can also - * be configured by setting the value of the property "sys:FPE" with - * Environment::set_value(). - * \param[in] flag set to \c true to enable FPEs, \c false to disable. - * \see FPE_enabled() - */ - void GEOGRAM_API enable_FPE(bool flag); - - /** - * \brief Gets the status of floating point exceptions - * \retval true if FPE are enabled - * \retval false otherwise - * \see enable_FPE() - */ - bool GEOGRAM_API FPE_enabled(); - - /** - * \brief Enables/disables multi-threaded computations - * Multi-threading can also be configured by setting the value of the - * property "sys:multithread" with Environment::set_value(). - * \param[in] flag set to \c true to enable multi-threading, \c false - * to disable. - * \see multithreading_enabled() - */ - void GEOGRAM_API enable_multithreading(bool flag); - - /** - * \brief Gets the status of multi-threading - * \retval true if multi-threading is enabled - * \retval false otherwise - * \see enable_multithreading() - */ - bool GEOGRAM_API multithreading_enabled(); - - /** - * \brief Limits the number of concurrent threads to use - * \details The number of threads can also be configured by setting - * the value of the property "sys:max_threads" with - * Environment::set_value(). - * \param[in] num_threads maximum number of threads to use. - * \see max_threads() - */ - void GEOGRAM_API set_max_threads(index_t num_threads); - - /** - * \brief Gets the number of allowed concurrent threads - * \see set_max_threads() - */ - index_t GEOGRAM_API max_threads(); - - /** - * \brief Enables interruption of cancelable tasks - * \details This allows to interrupt cancelable tasks by typing - * CTRL-C in the terminal. This sets a specific handler on the - * interrupt signal that calls Progress::cancel() is there is a - * running cancelable task. If no task is running, the program is - * interrupted. The cancel mode can also be configured by setting the - * value of the property "sys:cancel" with - * Environment::set_value(). - * \param[in] flag set to \c true to enable cancel mode, \c false - * to disable. - * \see cancel_enabled() - */ - void GEOGRAM_API enable_cancel(bool flag); - - /** - * \brief Gets the status of the cancel mode - * \retval true if the cancel mode is enabled - * \retval false otherwise - * \see enable_cancel() - */ - bool GEOGRAM_API cancel_enabled(); - - /** - * \brief Gets the currently used memory. - * \return the used memory in bytes - */ - size_t GEOGRAM_API used_memory(); - - /** - * \brief Gets the maximum used memory. - * \return the maximum used memory in bytes - */ - size_t GEOGRAM_API max_used_memory(); - - - /** - * \brief Gets the full path to the currently - * running program. - */ - std::string GEOGRAM_API executable_filename(); - } - - /** - * \brief Thread class used internally by parallel_for() - * \details ParallelForThread is a helper Thread class used internally by - * function parallel_for(). It executes a portion of the global loop - * controlled by parallel_for(). At each iteration, ParallelForThread - * executes an action specified by the template parameter Func - * which must be a functional object. - * \tparam Func functional object called at each iteration. It must accept a - * single argument of type index_t. - */ - template - class ParallelForThread : public Thread { - public: - /** - * \brief Creates a thread for the execution of a parallel_for() - * \param[in] func the functional object called at each iteration - * \param[in] from start index of the loop - * \param[in] to stop index of the loop - * \param[in] step loop step - */ - ParallelForThread( - const Func& func, index_t from, index_t to, index_t step = 1 - ) : - func_(func), - from_(from), - to_(to), - step_(step) { - } - - /** - * \brief Starts the thread execution - * \details It executes the portion of loop configured in the - * constructor, calling the functional object at each iteration with - * the current iteration index. - */ - virtual void run() { - for(index_t i = from_; i < to_; i += step_) { - const_cast (func_)(i); - } - } - - protected: - /** ParallelForThread destructor */ - virtual ~ParallelForThread() { - } - - private: - const Func& func_; - index_t from_; - index_t to_; - index_t step_; - }; - - - /** - * \brief Thread class used internally by parallel_for_slice() - * \details ParallelForThread is a helper Thread class used internally by - * function parallel_for_slice(). It executes a portion of the global loop - * controlled by parallel_for_slice(). At each iteration, ParallelForThread - * executes an action specified by the template parameter Func - * which must be a functional object. - * \tparam Func functional object called at each iteration. - * It must accept two arguments of type index_t. - */ - template - class ParallelForSliceThread : public Thread { - public: - /** - * \brief Creates a thread for the execution of a parallel_for() - * \param[in] func the functional object called at each iteration - * \param[in] from start index of the loop - * \param[in] to stop index of the loop - */ - ParallelForSliceThread( - const Func& func, index_t from, index_t to - ) : - func_(func), - from_(from), - to_(to) { - } - - /** - * \brief Starts the thread execution - * \details It executes the portion of loop configured in the - * constructor, calling the functional object at each iteration with - * the current iteration index. - */ - virtual void run() { - const_cast (func_)(from_, to_); - } - - protected: - /** ParallelForSliceThread destructor */ - virtual ~ParallelForSliceThread() { - } - - private: - const Func& func_; - index_t from_; - index_t to_; - }; - - - /** - * \brief Executes a loop with concurrent threads. - * - * Executes a parallel for loop from index \p to index \p to, calling - * functional object \p func at each iteration. - * - * Calling parallel_for(func, from, to) is equivalent - * to the following loop, computed in parallel: - * \code - * for(index_t i = from; i < to; i++) { - * func(i) - * } - * \endcode - * - * When applicable, iterations are executed by concurrent ParallelForThread - * threads: the range of the loop is split in to several contiguous - * sub-ranges, each of them being executed by a separate - * ParallelForThread. - * - * If parameter \p interleaved is set to true, the loop range is - * decomposed in interleaved index sets that are executed by the - * ParallelForThread. Interleaved execution may improve cache coherency. - * - * \param[in] func functional object that accepts a single argument of - * type index_t. - * \param[in] from start index of the loop - * \param[in] to stop index of the loop - * \param[in] threads_per_core number of threads to allocate per physical - * core (default is 1). - * \param[in] interleaved if set to \c true, indices are allocated to - * threads with an interleaved pattern. - * - * \tparam Func functional object called at each loop iteration. It - * must accept a single argument of type index_t. - * - * \see ParallelForThread - */ - template - inline void parallel_for( - index_t from, index_t to, const Func& func, - index_t threads_per_core = 1, - bool interleaved = false - ) { -#ifdef GEO_OS_WINDOWS - // TODO: This is a limitation of WindowsThreadManager, to be fixed. - threads_per_core = 1; -#endif - - index_t nb_threads = std::min( - to - from, - Process::maximum_concurrent_threads() * threads_per_core - ); - - nb_threads = std::max(index_t(1), nb_threads); - - index_t batch_size = (to - from) / nb_threads; - if(Process::is_running_threads() || nb_threads == 1) { - for(index_t i = from; i < to; i++) { - const_cast (func)(i); - } - } else { - ThreadGroup threads; - if(interleaved) { - for(index_t i = 0; i < nb_threads; i++) { - threads.push_back( - new ParallelForThread( - func, from + i, to, nb_threads - ) - ); - } - } else { - index_t cur = from; - for(index_t i = 0; i < nb_threads; i++) { - if(i == nb_threads - 1) { - threads.push_back( - new ParallelForThread( - func, cur, to - ) - ); - } else { - threads.push_back( - new ParallelForThread( - func, cur, cur + batch_size - ) - ); - } - cur += batch_size; - } - } - Process::run_threads(threads); - } - } - - - /** - * \brief Executes a loop with concurrent threads. - * - * When applicable, iterations are executed by concurrent ParallelForThread - * threads: the range of the loop is split in to several contiguous - * sub-ranges, each of them being executed by a separate - * ParallelForThread. - * - * \param[in] func functional object that accepts two arguments of - * type index_t. - * \param[in] from start index of the loop - * \param[in] to stop index of the loop - * \param[in] threads_per_core number of threads to allocate per physical - * core (default is 1). - * - * \tparam Func functional object called at each loop iteration. It - * must accept two arguments of type index_t. - * - * \see ParallelForSliceThread - */ - template - inline void parallel_for_slice( - index_t from, index_t to, const Func& func, - index_t threads_per_core = 1 - ) { -#ifdef GEO_OS_WINDOWS - // TODO: This is a limitation of WindowsThreadManager, to be fixed. - threads_per_core = 1; -#endif - - index_t nb_threads = std::min( - to - from, - Process::maximum_concurrent_threads() * threads_per_core - ); - - nb_threads = std::max(index_t(1), nb_threads); - - index_t batch_size = (to - from) / nb_threads; - if(Process::is_running_threads() || nb_threads == 1) { - const_cast (func)(from, to); - } else { - ThreadGroup threads; - index_t cur = from; - for(index_t i = 0; i < nb_threads; i++) { - if(i == nb_threads - 1) { - threads.push_back( - new ParallelForSliceThread( - func, cur, to - ) - ); - } else { - threads.push_back( - new ParallelForSliceThread( - func, cur, cur + batch_size - ) - ); - } - cur += batch_size; - } - Process::run_threads(threads); - } - } -} - -#endif - diff --git a/src/lib/geogram/basic/process.h.old b/src/lib/geogram/basic/process.h.old deleted file mode 100644 index 2473e451..00000000 --- a/src/lib/geogram/basic/process.h.old +++ /dev/null @@ -1,866 +0,0 @@ -/* - * Copyright (c) 2012-2014, Bruno Levy - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * * Neither the name of the ALICE Project-Team nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * If you modify this software, you should include a notice giving the - * name of the person performing the modification, the date of modification, - * and the reason for such modification. - * - * Contact: Bruno Levy - * - * Bruno.Levy@inria.fr - * http://www.loria.fr/~levy - * - * ALICE Project - * LORIA, INRIA Lorraine, - * Campus Scientifique, BP 239 - * 54506 VANDOEUVRE LES NANCY CEDEX - * FRANCE - * - */ - -#ifndef GEOGRAM_BASIC_PROCESS -#define GEOGRAM_BASIC_PROCESS - -#include -#include -#include -#include -#include - -/** - * \file geogram/basic/process.h - * \brief Function and classes for process manipulation - */ - -namespace GEO { - -// thread_local is supposed to be supported by c++0x, -// but some old MSVC compilers do not have it. -#if defined(GEO_COMPILER_MSVC) && !defined(thread_local) -# define thread_local __declspec(thread) -#endif - -// Older MAC OS X do not have thread_local -#ifdef GEO_OS_APPLE -# if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_9 -# define thread_local -# define GEO_NO_THREAD_LOCAL -# endif -#endif - - /** - * \brief Platform-independent base class for running threads. - * \details - * A Thread object manages one thread of control within the program. - * Thread%s begin executing with run(). Operational threads can be created - * by creating a derived class and reimplement function run(). - * - * Thread%s are reference-counted objects. Their allocation and - * destruction can be automatically managed with Thread_var. - */ - class GEOGRAM_API Thread : public Counted { - public: - - /** - * \brief Thread constructor. - */ - Thread() : id_(0) { - } - - /** - * \brief Starts the thread execution. - */ - virtual void run() = 0; - - /** - * \brief Gets the identifier of this thread. - * \return the identifier of the thread, i.e. - * an unsigned integer in the range [0, N-1] - * where N denotes the number of currently - * running threads. - */ - index_t id() const { - return id_; - } - - /** - * \brief Gets the current thread. - * \return A pointer to the instance of the - * currently running thread. - */ - static Thread* current(); - - protected: - /** Thread destructor */ - virtual ~Thread(); - - - private: - /** - * \brief Sets the identifier of this thread. - * \details This function is meant to be called - * by the thread manager for each created thread. - * \param[in] id_in the identifier of this thread. - */ - void set_id(index_t id_in) { - id_ = id_in; - } - - /** - * \brief Specifies the current instance, used by current(). - * \details Stores the specified thread in the thread-local-storage - * static variable so that current() can retrieve it. - * Should be called by the ThreadManager right before launching - * the threads. - * \param[in] thread a pointer to the thread currently executed - */ - static void set_current(Thread* thread); - - index_t id_; - - // ThreadManager needs to access set_current() and - // set_id(). - friend class ThreadManager; - }; - - /** Smart pointer that contains a Thread object */ - typedef SmartPointer Thread_var; - - /** - * \brief Collection of Thread%s - * \details ThreadGroup is a std::vector of Thread_var it provides the - * same operations for adding, removing or accessing thread elements. - * ThreadGroup takes ownership of Thread elements when they are added to - * the group, so there's is no need to delete Threads when the group is - * deleted. - */ - typedef std::vector ThreadGroup; - - /** - * \brief Typed collection of Thread%s. - * \details - * TypedThreadGroup is a ThreadGroup that provides a typed accessor with - * operator[](). - * \tparam THREAD the type of Thread%s in the collection - */ - template - class TypedThreadGroup : public ThreadGroup { - public: - /** - * \brief Creates an empty group of Thread%s - * \details Thread elements can be added with the std::vector - * operation push_back() - */ - TypedThreadGroup() { - } - - /** - * \brief Gets a thread element by index - * \param[in] i index of the element - * \return a pointer to the \p THREAD at position \p i in the - * thread group - */ - THREAD* operator[] (index_t i) { - geo_debug_assert(i < size()); - Thread* result = ThreadGroup::operator[] (i); - return static_cast(result); - } - }; - - /** - * \brief Platform-independent base class for running concurrent threads. - * \details - * The ThreadManager manager provides a platform-independent abstract - * interface for running concurrent Threads and managing critical - * sections. - * - * The ThreadManager is derived in multiple platform-specific or - * technology-specific implementations. - * - * Platform-specific implementations: - * - POSIX Thread manager (Unix) - * - Windows Threads manager (Windows) - * - Windows ThreadPool manager (Windows) - * - * Technology-specific implementations: - * - OpenMP-based manager - * - * Which ThreadManager to use is determined at runtime by - * Process::initialize() according to the current platform or the current - * available technology. - * - * \note For internal use only. - * \see Process::set_thread_manager() - */ - class GEOGRAM_API ThreadManager : public Counted { - public: - /** - * \brief Runs a group of Thread%s. - * \details - * This start the execution of the threads - * contained in vector \p threads. - * - * If the threads cannot be executed in a concurrent environment - * (multi-threading is disabled or the number of maximum threads is 1), - * then the threads are executed sequentially. Otherwise the function - * run_concurrent_threads() is called to execute the threads - * concurrently. The execution terminates when the last thread - * terminates. - * - * \param[in] threads the vector of threads to be executed. - * \see maximum_concurrent_threads() - * \see run_concurrent_threads() - * \see Process::max_threads() - */ - virtual void run_threads(ThreadGroup& threads); - - /** - * \brief Gets the maximum number of possible concurrent threads - * \return The maximum number of possible concurrent threads allowed - * by this manager. It depends on the physical number of cores - * (including hyper-threading or not) and the technology implemented - * by this manager. - * \see Process::number_of_cores() - */ - virtual index_t maximum_concurrent_threads() = 0; - - /** - * \brief Enters a critical section - * \details - * One thread at a time can enter the critical section, all the other - * threads that call this function are blocked until the blocking - * thread leaves the critical section. - * \see leave_critical_section() - */ - virtual void enter_critical_section() = 0; - - /** - * \brief Leaves a critical section - * \details When a blocking thread leaves a critical section, this - * makes the critical section available for a waiting thread. - * \see enter_critical_section() - */ - virtual void leave_critical_section() = 0; - - protected: - /** - * \brief Runs a group of Thread%s concurrently. - * \details This start the concurrent execution of the threads - * contained in vector \p threads, using the given number of threads - * \p max_threads. The execution terminates when the last thread - * terminates. - * \param[in] threads the vector of threads to be executed. - * \param[in] max_threads maximum number of threads allowed for this - * execution. It is always greater than one - */ - virtual void run_concurrent_threads( - ThreadGroup& threads, index_t max_threads - ) = 0; - - - /** - * \brief Sets the id of a thread. - * \details This function is called right before starting - * the threads. Each thread will have an id in [0, N-1] - * where N denotes the number of running threads. - * \param[in] thread the thread - * \param[in] id the id - */ - static void set_thread_id(Thread* thread, index_t id) { - thread->set_id(id); - } - - /** - * \brief Specifies the current instance, used by current(). - * \details Stores the specified thread in the thread-local-storage - * static variable so that current() can retrieve it. - * Should be called by the ThreadManager right before launching - * the threads. - * \param[in] thread a pointer to the thread currently executed - */ - static void set_current_thread(Thread* thread) { - Thread::set_current(thread); - } - - /** ThreadManager destructor */ - virtual ~ThreadManager(); - }; - - /** Smart pointer that contains a ThreadManager object */ - typedef SmartPointer ThreadManager_var; - - /** - * \brief Single thread ThreadManager - * \details MonoThreadingThreadManager implements a ThreadManager for - * single thread environments. - */ - class GEOGRAM_API MonoThreadingThreadManager : public ThreadManager { - public: - /** - * \copydoc ThreadManager::maximum_concurrent_threads() - * \note This implementation always returns 1. - */ - virtual index_t maximum_concurrent_threads(); - - /** - * \copydoc ThreadManager::enter_critical_section() - * \note This implementation does actually nothing - */ - virtual void enter_critical_section(); - - /** - * \copydoc ThreadManager::leave_critical_section() - * \note This implementation does actually nothing - */ - virtual void leave_critical_section(); - - protected: - /** MonoThreadingThreadManager destructor */ - virtual ~MonoThreadingThreadManager(); - - /** - * \copydoc ThreadManager::run_concurrent_threads() - * \note This implementation always executes threads sequentially. - */ - virtual void run_concurrent_threads( - ThreadGroup& threads, index_t max_threads - ); - }; - - /** - * \brief Abstraction layer for process management and multi-threading. - */ - namespace Process { - - /** - * \brief Initializes GeogramLib - * \param[in] flags the flags passed to GEO::initialize() - * \details This function must be called once before using - * any functionality of GeogramLib. - */ - void GEOGRAM_API initialize(int flags); - - /** - * \brief Terminates GeogramLib - * \details This function is called automatically when the program - * exits, so it should never be called directly. - */ - void GEOGRAM_API terminate(); - - /** - * \brief Displays statistics about the current process - * \details Displays the maximum used amount of memory. - */ - void GEOGRAM_API show_stats(); - - /** - * \brief Terminates the current process. - */ - void GEOGRAM_API brute_force_kill(); - - /** - * \brief Returns the maximum number of threads that can be running - * simultaneously. - * \retval The number of cores if multi-threading is supported - * \retval 1 otherwise. - */ - index_t GEOGRAM_API maximum_concurrent_threads(); - - /** - * \brief Runs a set of threads simultaneously - * \details Launches the execution of the threads contained in the - * vector \p threads and waits for the completion of all of them. - */ - void GEOGRAM_API run_threads(ThreadGroup& threads); - - /** - * \brief Enters a critical section - * \details One thread at a time can enter the critical section, - * all the other threads that call this function are blocked until the - * blocking thread leaves the critical section - * \see ThreadManager::enter_critical_section() - * \see leave_critical_section() - */ - void GEOGRAM_API enter_critical_section(); - - /** - * \brief Leaves a critical section - * \details When a blocking thread leaves a critical section, this - * makes the critical section available for a waiting thread. - * \see ThreadManager::leave_critical_section() - * \see enter_critical_section() - */ - void GEOGRAM_API leave_critical_section(); - - /** - * \brief Gets the number of available cores - * \return The number of available cores including the "virtual ones" if - * hyper-threading is activated. - */ - index_t GEOGRAM_API number_of_cores(); - - /** - * \brief Sets the thread manager (internal use). - * \details This sets the ThreadManager to use for concurrent thread - * execution. This function is called internally by - * Process::initialize() and should not be called explicitly. - * \note For internal use only - */ - void GEOGRAM_API set_thread_manager(ThreadManager* thread_manager); - - /** - * \brief Checks whether threads are running. - * \retval true if concurrent threads are currently running as an - * effect to Process::run_threads(). - * \retval false otherwise. - * \see Process::run_threads() - */ - bool GEOGRAM_API is_running_threads(); - - /** - * \brief Enables/disables floating point exceptions - * \details If FPEs are enabled, then floating point exceptions - * raise a SIGFPE signal, otherwise they generate NaNs. FPEs can also - * be configured by setting the value of the property "sys:FPE" with - * Environment::set_value(). - * \param[in] flag set to \c true to enable FPEs, \c false to disable. - * \see FPE_enabled() - */ - void GEOGRAM_API enable_FPE(bool flag); - - /** - * \brief Gets the status of floating point exceptions - * \retval true if FPE are enabled - * \retval false otherwise - * \see enable_FPE() - */ - bool GEOGRAM_API FPE_enabled(); - - /** - * \brief Enables/disables multi-threaded computations - * Multi-threading can also be configured by setting the value of the - * property "sys:multithread" with Environment::set_value(). - * \param[in] flag set to \c true to enable multi-threading, \c false - * to disable. - * \see multithreading_enabled() - */ - void GEOGRAM_API enable_multithreading(bool flag); - - /** - * \brief Gets the status of multi-threading - * \retval true if multi-threading is enabled - * \retval false otherwise - * \see enable_multithreading() - */ - bool GEOGRAM_API multithreading_enabled(); - - /** - * \brief Limits the number of concurrent threads to use - * \details The number of threads can also be configured by setting - * the value of the property "sys:max_threads" with - * Environment::set_value(). - * \param[in] num_threads maximum number of threads to use. - * \see max_threads() - */ - void GEOGRAM_API set_max_threads(index_t num_threads); - - /** - * \brief Gets the number of allowed concurrent threads - * \see set_max_threads() - */ - index_t GEOGRAM_API max_threads(); - - /** - * \brief Enables interruption of cancelable tasks - * \details This allows to interrupt cancelable tasks by typing - * CTRL-C in the terminal. This sets a specific handler on the - * interrupt signal that calls Progress::cancel() is there is a - * running cancelable task. If no task is running, the program is - * interrupted. The cancel mode can also be configured by setting the - * value of the property "sys:cancel" with - * Environment::set_value(). - * \param[in] flag set to \c true to enable cancel mode, \c false - * to disable. - * \see cancel_enabled() - */ - void GEOGRAM_API enable_cancel(bool flag); - - /** - * \brief Gets the status of the cancel mode - * \retval true if the cancel mode is enabled - * \retval false otherwise - * \see enable_cancel() - */ - bool GEOGRAM_API cancel_enabled(); - - /** - * \brief Gets the currently used memory. - * \return the used memory in bytes - */ - size_t GEOGRAM_API used_memory(); - - /** - * \brief Gets the maximum used memory. - * \return the maximum used memory in bytes - */ - size_t GEOGRAM_API max_used_memory(); - - - /** - * \brief Gets the full path to the currently - * running program. - */ - std::string GEOGRAM_API executable_filename(); - } - - /** - * \brief Thread class used internally by parallel_for() - * \details ParallelForThread is a helper Thread class used internally by - * function parallel_for(). It executes a portion of the global loop - * controlled by parallel_for(). At each iteration, ParallelForThread - * executes an action specified by the template parameter Func - * which must be a functional object. - * \tparam Func functional object called at each iteration. It must accept a - * single argument of type index_t. - */ - template - class ParallelForThread : public Thread { - public: - /** - * \brief Creates a thread for the execution of a parallel_for() - * \param[in] func the functional object called at each iteration - * \param[in] from start index of the loop - * \param[in] to stop index of the loop - * \param[in] step loop step - */ - ParallelForThread( - const Func& func, index_t from, index_t to, index_t step = 1 - ) : - func_(func), - from_(from), - to_(to), - step_(step) { - } - - /** - * \brief Starts the thread execution - * \details It executes the portion of loop configured in the - * constructor, calling the functional object at each iteration with - * the current iteration index. - */ - virtual void run() { - for(index_t i = from_; i < to_; i += step_) { - const_cast (func_)(i); - } - } - - protected: - /** ParallelForThread destructor */ - virtual ~ParallelForThread() { - } - - private: - const Func& func_; - index_t from_; - index_t to_; - index_t step_; - }; - - - /** - * \brief Thread class used internally by parallel_for_slice() - * \details ParallelForThread is a helper Thread class used internally by - * function parallel_for_slice(). It executes a portion of the global loop - * controlled by parallel_for_slice(). At each iteration, ParallelForThread - * executes an action specified by the template parameter Func - * which must be a functional object. - * \tparam Func functional object called at each iteration. - * It must accept two arguments of type index_t. - */ - template - class ParallelForSliceThread : public Thread { - public: - /** - * \brief Creates a thread for the execution of a parallel_for() - * \param[in] func the functional object called at each iteration - * \param[in] from start index of the loop - * \param[in] to stop index of the loop - */ - ParallelForSliceThread( - const Func& func, index_t from, index_t to - ) : - func_(func), - from_(from), - to_(to) { - } - - /** - * \brief Starts the thread execution - * \details It executes the portion of loop configured in the - * constructor, calling the functional object at each iteration with - * the current iteration index. - */ - virtual void run() { - const_cast (func_)(from_, to_); - } - - protected: - /** ParallelForSliceThread destructor */ - virtual ~ParallelForSliceThread() { - } - - private: - const Func& func_; - index_t from_; - index_t to_; - }; - - - /** - * \brief Executes a loop with concurrent threads. - * - * Executes a parallel for loop from index \p to index \p to, calling - * functional object \p func at each iteration. - * - * Calling parallel_for(func, from, to) is equivalent - * to the following loop, computed in parallel: - * \code - * for(index_t i = from; i < to; i++) { - * func(i) - * } - * \endcode - * - * When applicable, iterations are executed by concurrent ParallelForThread - * threads: the range of the loop is split in to several contiguous - * sub-ranges, each of them being executed by a separate - * ParallelForThread. - * - * If parameter \p interleaved is set to true, the loop range is - * decomposed in interleaved index sets that are executed by the - * ParallelForThread. Interleaved execution may improve cache coherency. - * - * \param[in] func functional object that accepts a single argument of - * type index_t. - * \param[in] from start index of the loop - * \param[in] to stop index of the loop - * \param[in] threads_per_core number of threads to allocate per physical - * core (default is 1). - * \param[in] interleaved if set to \c true, indices are allocated to - * threads with an interleaved pattern. - * - * \tparam Func functional object called at each loop iteration. It - * must accept a single argument of type index_t. - * - * \see ParallelForThread - */ - template - inline void parallel_for( - index_t from, index_t to, const Func& func, - index_t threads_per_core = 1, - bool interleaved = false - ) { -#ifdef GEO_OS_WINDOWS - // TODO: This is a limitation of WindowsThreadManager, to be fixed. - threads_per_core = 1; -#endif - - index_t nb_threads = std::min( - to - from, - Process::maximum_concurrent_threads() * threads_per_core - ); - - nb_threads = std::max(index_t(1), nb_threads); - - index_t batch_size = (to - from) / nb_threads; - if(Process::is_running_threads() || nb_threads == 1) { - for(index_t i = from; i < to; i++) { - const_cast (func)(i); - } - } else { - ThreadGroup threads; - if(interleaved) { - for(index_t i = 0; i < nb_threads; i++) { - threads.push_back( - new ParallelForThread( - func, from + i, to, nb_threads - ) - ); - } - } else { - index_t cur = from; - for(index_t i = 0; i < nb_threads; i++) { - if(i == nb_threads - 1) { - threads.push_back( - new ParallelForThread( - func, cur, to - ) - ); - } else { - threads.push_back( - new ParallelForThread( - func, cur, cur + batch_size - ) - ); - } - cur += batch_size; - } - } - Process::run_threads(threads); - } - } - - - /** - * \brief Executes a loop with concurrent threads. - * - * When applicable, iterations are executed by concurrent ParallelForThread - * threads: the range of the loop is split in to several contiguous - * sub-ranges, each of them being executed by a separate - * ParallelForThread. - * - * \param[in] func functional object that accepts two arguments of - * type index_t. - * \param[in] from start index of the loop - * \param[in] to stop index of the loop - * \param[in] threads_per_core number of threads to allocate per physical - * core (default is 1). - * - * \tparam Func functional object called at each loop iteration. It - * must accept two arguments of type index_t. - * - * \see ParallelForSliceThread - */ - template - inline void parallel_for_slice( - index_t from, index_t to, const Func& func, - index_t threads_per_core = 1 - ) { -#ifdef GEO_OS_WINDOWS - // TODO: This is a limitation of WindowsThreadManager, to be fixed. - threads_per_core = 1; -#endif - - index_t nb_threads = std::min( - to - from, - Process::maximum_concurrent_threads() * threads_per_core - ); - - nb_threads = std::max(index_t(1), nb_threads); - - index_t batch_size = (to - from) / nb_threads; - if(Process::is_running_threads() || nb_threads == 1) { - const_cast (func)(from, to); - } else { - ThreadGroup threads; - index_t cur = from; - for(index_t i = 0; i < nb_threads; i++) { - if(i == nb_threads - 1) { - threads.push_back( - new ParallelForSliceThread( - func, cur, to - ) - ); - } else { - threads.push_back( - new ParallelForSliceThread( - func, cur, cur + batch_size - ) - ); - } - cur += batch_size; - } - Process::run_threads(threads); - } - } - - /** - * \brief Used to implement parallel_for_member_callback() - * \details Stores a pointer to an object and to a pointer - * function for invoking it later. - * \tparam T object class - */ - template class ParallelForMemberCallback { - public: - /** - * \brief Function pointer type. - */ - typedef void (T::*fptr)(index_t); - - /** - * \brief ParallelForMemberCallback constructor. - * \param[in] object a pointer to an object - * \param[in] f a pointer to a member function of the object, - * that takes an index_t as an argument - */ - ParallelForMemberCallback(T* object, fptr f) : - object_(object), f_(f) { - } - - /** - * \brief Invokes the memorized function on the memorized object - * with the argument \p i - * \param[in] i the argument to be passed to the function. - */ - void operator()(index_t i) { - (*object_.*f_)(i); - } - private: - T* object_; - fptr f_; - }; - - /** - * \brief Creates a member callback for parallel_for() - * \details This allows to run parallel_for() with a functional object - * that calls the member function \p fun on object \p obj. - * \par Example: - * \code - * struct MyClass { - * void iteration(index_t i) { ... } - * }; - * MyClass obj; - * parallel_for( - * parallel_for_member_callback(&obj, &MyClass::iteration), - * from, to - * ); - * \endcode - * \param[in] obj pointer to an object of type T - * \param[in] fun pointer to a void member function of object \p obj that - * accepts a single argument of type index_t. - * \tparam T type of the target object - */ - - template ParallelForMemberCallback - parallel_for_member_callback(T* obj, void (T::* fun)(index_t)) { - return ParallelForMemberCallback(obj, fun); - } - -} - -#endif - diff --git a/src/lib/geogram/basic/thread_sync.h b/src/lib/geogram/basic/thread_sync.h index fb9c4787..41b12cc4 100644 --- a/src/lib/geogram/basic/thread_sync.h +++ b/src/lib/geogram/basic/thread_sync.h @@ -745,6 +745,7 @@ namespace GEO { return 0; } + // Emulation of pthread condition variables using Windows API typedef CONDITION_VARIABLE pthread_cond_t; @@ -770,7 +771,7 @@ namespace GEO { SleepConditionVariableCS(c, m, INFINITE); return 0; } - + #endif } diff --git a/src/lib/geogram/delaunay/periodic_delaunay_3d.cpp b/src/lib/geogram/delaunay/periodic_delaunay_3d.cpp index 496da3f1..4aed84e1 100644 --- a/src/lib/geogram/delaunay/periodic_delaunay_3d.cpp +++ b/src/lib/geogram/delaunay/periodic_delaunay_3d.cpp @@ -2887,6 +2887,8 @@ namespace GEO { void PeriodicDelaunay3d::set_vertices( index_t nb_vertices, const double* vertices ) { + has_empty_cells_ = false; + #ifndef GARGANTUA { Numeric::uint64 expected_max_index = @@ -2935,6 +2937,7 @@ namespace GEO { } void PeriodicDelaunay3d::set_weights(const double* weights) { + has_empty_cells_ = false; weights_ = weights; } @@ -3818,12 +3821,12 @@ namespace GEO { // boundary facets on which the vertex resides. if(vertex_on_boundary) { cell_is_on_boundary = true; - for(int U=0; U<2; ++U) { - for(int V=0; V<2; ++V) { - for(int W=0; W<2; ++W) { - int Tx = U*VXLAT[0][0] + V*VXLAT[1][0] + W*VXLAT[2][0]; - int Ty = U*VXLAT[0][1] + V*VXLAT[1][1] + W*VXLAT[2][1]; - int Tz = U*VXLAT[0][2] + V*VXLAT[1][2] + W*VXLAT[2][2]; + for(int dU=0; dU<2; ++dU) { + for(int dV=0; dV<2; ++dV) { + for(int dW=0; dW<2; ++dW) { + int Tx = dU*VXLAT[0][0] + dV*VXLAT[1][0] + dW*VXLAT[2][0]; + int Ty = dU*VXLAT[0][1] + dV*VXLAT[1][1] + dW*VXLAT[2][1]; + int Tz = dU*VXLAT[0][2] + dV*VXLAT[1][2] + dW*VXLAT[2][2]; use_instance[T_to_instance(Tx,Ty,Tz)] = true; } } diff --git a/src/lib/geogram/delaunay/periodic_delaunay_3d.h b/src/lib/geogram/delaunay/periodic_delaunay_3d.h index 50cbf795..442ab2a3 100755 --- a/src/lib/geogram/delaunay/periodic_delaunay_3d.h +++ b/src/lib/geogram/delaunay/periodic_delaunay_3d.h @@ -215,7 +215,7 @@ namespace GEO { * \brief computes the set of tetrahedra that are incident to * a vertex. * \param[in] v the index of the vertex. - * \param[in,out] Workspace a reference to a PeriodicDelaunay3d::Workspace. + * \param[in,out] W a reference to a PeriodicDelaunay3d::IncidentTetrahedra. * On exit it contains the list of incident tets. */ void get_incident_tets(index_t v, IncidentTetrahedra& W) const; @@ -286,7 +286,7 @@ namespace GEO { * \param[in] t a tetrahedron of the Delaunay triangulation, * incident to vertex i * \param[out] C the Laguerre cell. - * \param[in,out] Workspace a reference to a PeriodicDelaunay3d::IncidentTetrahedra + * \param[in,out] W a reference to a PeriodicDelaunay3d::IncidentTetrahedra * \return the local index of vertex \p i within tetrahedron \p t */ GEO::index_t copy_Laguerre_cell_facet_from_Delaunay( diff --git a/src/lib/geogram/image/image_serializer_xpm.cpp b/src/lib/geogram/image/image_serializer_xpm.cpp index 0f3ad0d1..318645b1 100755 --- a/src/lib/geogram/image/image_serializer_xpm.cpp +++ b/src/lib/geogram/image/image_serializer_xpm.cpp @@ -45,22 +45,32 @@ namespace { using namespace GEO; + /** + * \brief Converts an hexadecimal digit into an integer. + * \param[in] digit the character representation of the digit. + * \return the corresonding integer value, in [0,15]. + */ inline int htoi(char digit) { if(digit >= '0' && digit <= '9') { - return digit - '0' ; + return digit - '0'; } if(digit >= 'a' && digit <= 'f') { - return digit - 'a' + 10 ; + return digit - 'a' + 10; } if(digit >= 'A' && digit <= 'F') { - return digit - 'A' + 10 ; + return digit - 'A' + 10; } Logger::err("Image") << "XPM Image reader: hex digit to integer: invalid digit: \'" - << digit << "\'" << std::endl ; - abort() ; + << digit << "\'" << std::endl; + abort(); } + /** + * \brief Decodes an XPM colormap entry. + * \param[in] the string with the colormap entry. + * \param[out] colorcell the corresponding color. + */ bool decode_colormap_entry( const char* colormap_entry, Colormap::ColorCell& colorcell ) { @@ -72,25 +82,25 @@ namespace { } else { Logger::err("Image") << "XPM Image reader: Colormap entry without any color" - << std::endl ; + << std::endl; Logger::err("Image") - << " entry = \'" << colormap_entry << "\'" << std::endl ; + << " entry = \'" << colormap_entry << "\'" << std::endl; return false; } } - colorcode += 3 ; + colorcode += 3; Memory::byte r,g,b,a; if(strlen(colorcode) == 12) { - r = Memory::byte(16 * htoi(colorcode[0]) + htoi(colorcode[1])) ; - g = Memory::byte(16 * htoi(colorcode[4]) + htoi(colorcode[5])) ; - b = Memory::byte(16 * htoi(colorcode[8]) + htoi(colorcode[9])) ; + r = Memory::byte(16 * htoi(colorcode[0]) + htoi(colorcode[1])); + g = Memory::byte(16 * htoi(colorcode[4]) + htoi(colorcode[5])); + b = Memory::byte(16 * htoi(colorcode[8]) + htoi(colorcode[9])); a = 255; } else { - r = Memory::byte(16 * htoi(colorcode[0]) + htoi(colorcode[1])) ; - g = Memory::byte(16 * htoi(colorcode[2]) + htoi(colorcode[3])) ; - b = Memory::byte(16 * htoi(colorcode[4]) + htoi(colorcode[5])) ; + r = Memory::byte(16 * htoi(colorcode[0]) + htoi(colorcode[1])); + g = Memory::byte(16 * htoi(colorcode[2]) + htoi(colorcode[3])); + b = Memory::byte(16 * htoi(colorcode[4]) + htoi(colorcode[5])); a = 255; } colorcell = Colormap::ColorCell(r,g,b,a); @@ -99,63 +109,65 @@ namespace { } -namespace GEO { - -//_________________________________________________________ - - +/******************************************************************************/ +namespace GEO { Image* ImageSerializer_xpm::serialize_read(std::istream& stream) { + return serialize_read_static(stream); + } + + Image* ImageSerializer_xpm::create_image_from_xpm_data(const char* s) { + std::istringstream in(s); + return serialize_read_static(in); + } + + Image* ImageSerializer_xpm::serialize_read_static(std::istream& stream) { Image* result = nullptr; - // Well, ugly code ahead, - // sorry about that, but I really needed - // an XPM image reader ... + int num_colors; + int chars_per_pixels; + int width; + int height; - int num_colors ; - int chars_per_pixels ; - int width ; - int height ; - - // _______________________ header + // *********************** header { - char* header = next_xpm_data(stream) ; + char* header = next_xpm_data(stream); if(header == nullptr) { Logger::err("Image") - << "XPM image input: unexpected end of file" << std::endl ; - return nullptr ; + << "XPM image input: unexpected end of file" << std::endl; + return nullptr; } - std::istringstream in(header) ; - in >> width >> height >> num_colors >> chars_per_pixels ; - if(num_colors > 256) { + std::istringstream in(header); + in >> width >> height >> num_colors >> chars_per_pixels; + if(num_colors > 1024) { Logger::err("Image") << "XPM image input: too many colors (" << num_colors - << ")" << std::endl ; + << ")" << std::endl; Logger::err("Image") - << " should not be greater than 256" << std::endl ; - return nullptr ; + << " should not be greater than 1024" << std::endl; + return nullptr; } switch(chars_per_pixels) { case 1: result=read_xpm_1_byte_per_pixel( width, height, num_colors, stream - ) ; - break ; + ); + break; case 2: result=read_xpm_2_bytes_per_pixel( width, height, num_colors, stream - ) ; - break ; + ); + break; default: Logger::err("Image") << "XPM image input: invalid chars per pixels (" - << chars_per_pixels << ")" << std::endl ; - Logger::err("Image") << " should be 2" << std::endl ; - return nullptr ; + << chars_per_pixels << ")" << std::endl; + Logger::err("Image") << " should be 2" << std::endl; + return nullptr; } // Convert colormapped to RGBA. @@ -183,34 +195,34 @@ namespace GEO { // Converts a two-digit XPM color code into // a color index. - static int conv_table[256][256] ; + static int conv_table[256][256]; // For checking, put a negative value to // detect invalid color codes. for(int k1=0; k1 < 256; k1++) { for(int k2=0; k2 < 256; k2++) { - conv_table[k1][k2] = -1 ; + conv_table[k1][k2] = -1; } } - // _______________________ colormap + // ********************** colormap - typedef Numeric::uint8 byte ; + typedef Numeric::uint8 byte; - Colormap* colormap = new Colormap(index_t(num_colors)) ; + Colormap* colormap = new Colormap(index_t(num_colors)); for(int entry_num=0; entry_num color_cell(index_t(entry_num)) = cell; - conv_table[key1][key2] = (unsigned char)entry_num ; + conv_table[key1][key2] = (unsigned char)entry_num; } - // _______________________ image + // ****************** image Image* result = new Image( Image::INDEXED, Image::BYTE, index_t(width), index_t(height) - ) ; - result-> set_colormap(colormap) ; + ); + result-> set_colormap(colormap); for(int y=0; y num_colors) { Logger::err("Image") << "XPM Image reader: Invalid color index in image" - << std::endl ; - delete result ; - return nullptr ; + << std::endl; + delete result; + return nullptr; } - result-> base_mem()[y * width + x] = byte(color_index) ; + result-> base_mem()[y * width + x] = byte(color_index); } } - - result->flip_vertically(); - return result ; + + return result; } @@ -263,99 +274,99 @@ namespace GEO { // Converts a two-digit XPM color code into // a color index. - static int conv_table[256] ; + static int conv_table[256]; // For checking, put a negative value to // detect invalid color codes. for(int k1=0; k1 < 256; k1++) { - conv_table[k1] = -1 ; + conv_table[k1] = -1; } - // _______________________ colormap + // ********************* colormap - typedef Numeric::uint8 byte ; + typedef Numeric::uint8 byte; - Colormap* colormap = new Colormap(index_t(num_colors)) ; + Colormap* colormap = new Colormap(index_t(num_colors)); for(int entry_num=0; entry_num color_cell(index_t(entry_num)) = cell; - conv_table[key1] = (unsigned char)entry_num ; + conv_table[key1] = (unsigned char)entry_num; } - // _______________________ image + // ********************* image Image* result = new Image( Image::INDEXED, Image::BYTE, index_t(width), index_t(height) - ) ; - result-> set_colormap(colormap) ; + ); + result-> set_colormap(colormap); for(int y=0; y num_colors) { Logger::err("Image") << "XPM Image reader: Invalid color index in image" - << std::endl ; - delete result ; - return nullptr ; + << std::endl; + delete result; + return nullptr; } - result-> base_mem()[y * width + x] = byte(color_index) ; + result-> base_mem()[y * width + x] = byte(color_index); } } - return result ; + return result; } bool ImageSerializer_xpm::binary() const { - return false ; + return false; } char* ImageSerializer_xpm::next_xpm_data(std::istream& input) { - static char line_buffer[4096] ; - char* result = nullptr ; - bool found = false ; + static char line_buffer[4096]; + char* result = nullptr; + bool found = false; while(!found && !input.eof()) { - input.getline(line_buffer,4096) ; - char* p1 = strchr(line_buffer,'\"') ; - char* p2 = strchr(line_buffer + 1, '\"') ; - found = (p1 != nullptr && p2 != nullptr) ; + input.getline(line_buffer,4096); + char* p1 = strchr(line_buffer,'\"'); + char* p2 = strchr(line_buffer + 1, '\"'); + found = (p1 != nullptr && p2 != nullptr); if(found) { - result = p1 + 1 ; - *p2 = '\0' ; + result = p1 + 1; + *p2 = '\0'; } } - return result ; + return result; } bool ImageSerializer_xpm::read_supported() const { - return true ; + return true; } -//_________________________________________________________ +/**********************************************************************/ } diff --git a/src/lib/geogram/image/image_serializer_xpm.h b/src/lib/geogram/image/image_serializer_xpm.h index cdad372f..90269f93 100755 --- a/src/lib/geogram/image/image_serializer_xpm.h +++ b/src/lib/geogram/image/image_serializer_xpm.h @@ -46,23 +46,33 @@ namespace GEO { class GEOGRAM_API ImageSerializer_xpm : public ImageSerializer { public: - virtual Image* serialize_read(std::istream& stream) ; - virtual bool read_supported() const ; - virtual bool binary() const ; + virtual Image* serialize_read(std::istream& stream); + virtual bool read_supported() const; + virtual bool binary() const; + + /** + * \brief Creates an image from XPM data. + * \param[in] s the string with the XPM data. + * \return the created image. + */ + static Image* create_image_from_xpm_data(const char* s); protected: - Image* read_xpm_1_byte_per_pixel( + + static Image* serialize_read_static(std::istream& stream); + + static Image* read_xpm_1_byte_per_pixel( int width, int height, int num_colors, std::istream& input - ) ; + ); - Image* read_xpm_2_bytes_per_pixel( + static Image* read_xpm_2_bytes_per_pixel( int width, int height, int num_colors, std::istream& input - ) ; + ); - static char* next_xpm_data(std::istream& input) ; - } ; + static char* next_xpm_data(std::istream& input); + }; //_________________________________________________________ diff --git a/src/lib/geogram/mesh/mesh.cpp b/src/lib/geogram/mesh/mesh.cpp index 4b66440a..f3e574fe 100644 --- a/src/lib/geogram/mesh/mesh.cpp +++ b/src/lib/geogram/mesh/mesh.cpp @@ -1701,16 +1701,24 @@ namespace GEO { } void MeshCells::compute_borders() { + Attribute facet_cell; + compute_borders(facet_cell); + } + + void MeshCells::compute_borders(Attribute& facet_cell) { mesh_.facets.clear(true,false); if(is_simplicial_) { for(index_t t=0; t& facet_cell); + /** * \brief Copies a tetrahedron mesh into this Mesh. * \details Tetrahedron adjacences are not computed. diff --git a/src/lib/geogram/mesh/mesh_AABB.cpp b/src/lib/geogram/mesh/mesh_AABB.cpp index e7a1b514..34a101d3 100644 --- a/src/lib/geogram/mesh/mesh_AABB.cpp +++ b/src/lib/geogram/mesh/mesh_AABB.cpp @@ -297,17 +297,13 @@ namespace { * \param[in] M a const reference to the mesh * \param[in] t the index of the tetrahedron in \p M * \param[in] p a const reference to the point - * \param[in] exact specifies whether exact predicates should be used * \retval true if the tetrahedron \p t or its boundary contains * the point \p p * \retval false otherwise */ bool mesh_tet_contains_point( - const Mesh& M, index_t t, const vec3& p, bool exact = true + const Mesh& M, index_t t, const vec3& p ) { - // Inexact mode is not implemented yet. - geo_argused(exact); - const vec3& p0 = Geom::mesh_vertex(M, M.cells.vertex(t,0)); const vec3& p1 = Geom::mesh_vertex(M, M.cells.vertex(t,1)); const vec3& p2 = Geom::mesh_vertex(M, M.cells.vertex(t,2)); @@ -757,7 +753,7 @@ namespace GEO { } index_t MeshCellsAABB::containing_tet_recursive( - const vec3& p, bool exact, + const vec3& p, index_t n, index_t b, index_t e ) const { @@ -766,7 +762,7 @@ namespace GEO { } if(e==b+1) { - if(mesh_tet_contains_point(mesh_, b, p, exact)) { + if(mesh_tet_contains_point(mesh_, b, p)) { return b; } else { return NO_TET; @@ -778,10 +774,10 @@ namespace GEO { index_t childr = 2 * n + 1; index_t result = containing_tet_recursive( - p, exact, childl, b, m + p, childl, b, m ); if(result == NO_TET) { - result = containing_tet_recursive(p, exact, childr, m, e); + result = containing_tet_recursive(p, childr, m, e); } return result; } diff --git a/src/lib/geogram/mesh/mesh_AABB.cpp.debug b/src/lib/geogram/mesh/mesh_AABB.cpp.debug new file mode 100644 index 00000000..ecf40cfb --- /dev/null +++ b/src/lib/geogram/mesh/mesh_AABB.cpp.debug @@ -0,0 +1,895 @@ +/* + * Copyright (c) 2012-2014, Bruno Levy + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the ALICE Project-Team nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * If you modify this software, you should include a notice giving the + * name of the person performing the modification, the date of modification, + * and the reason for such modification. + * + * Contact: Bruno Levy + * + * Bruno.Levy@inria.fr + * http://www.loria.fr/~levy + * + * ALICE Project + * LORIA, INRIA Lorraine, + * Campus Scientifique, BP 239 + * 54506 VANDOEUVRE LES NANCY CEDEX + * FRANCE + * + */ + +#include +#include +#include +#include +#include +#include + +#ifdef __SSE2__ +#include +#endif + +#ifdef __AVX2__ +#include +#endif + +namespace { + + using namespace GEO; + + + /** + * \brief Computes the axis-aligned bounding box of a mesh facet. + * \param[in] M the mesh + * \param[out] B the bounding box of the facet + * \param[in] f the index of the facet in mesh \p M + */ + void get_facet_bbox( + const Mesh& M, Box& B, index_t f + ) { + index_t c = M.facets.corners_begin(f); + const double* p = M.vertices.point_ptr(M.facet_corners.vertex(c)); + for(coord_index_t coord = 0; coord < 3; ++coord) { + B.xyz_min[coord] = p[coord]; + B.xyz_max[coord] = p[coord]; + } + for(++c; c < M.facets.corners_end(f); ++c) { + p = M.vertices.point_ptr(M.facet_corners.vertex(c)); + for(coord_index_t coord = 0; coord < 3; ++coord) { + B.xyz_min[coord] = std::min(B.xyz_min[coord], p[coord]); + B.xyz_max[coord] = std::max(B.xyz_max[coord], p[coord]); + } + } + } + + + /** + * \brief Computes the axis-aligned bounding box of a mesh tetrahedron. + * \param[in] M the mesh + * \param[out] B the bounding box of the facet + * \param[in] t the index of the tetrahedron in mesh \p M + */ + void get_tet_bbox( + const Mesh& M, Box& B, index_t t + ) { + const double* p = M.vertices.point_ptr(M.cells.vertex(t,0)); + for(coord_index_t coord = 0; coord < 3; ++coord) { + B.xyz_min[coord] = p[coord]; + B.xyz_max[coord] = p[coord]; + } + for(index_t lv=1; lv<4; ++lv) { + p = M.vertices.point_ptr(M.cells.vertex(t,lv)); + for(coord_index_t coord = 0; coord < 3; ++coord) { + B.xyz_min[coord] = std::min(B.xyz_min[coord], p[coord]); + B.xyz_max[coord] = std::max(B.xyz_max[coord], p[coord]); + } + } + } + + /** + * \brief Computes the axis-aligned bounding box of a mesh cell + * \param[in] M the mesh + * \param[out] B the bounding box of the facet + * \param[in] c the index of the cell in mesh \p M + */ + void get_cell_bbox( + const Mesh& M, Box& B, index_t c + ) { + const double* p = M.vertices.point_ptr(M.cells.vertex(c,0)); + for(coord_index_t coord = 0; coord < 3; ++coord) { + B.xyz_min[coord] = p[coord]; + B.xyz_max[coord] = p[coord]; + } + for(index_t lv=1; lv b); + if(b + 1 == e) { + return node_index; + } + index_t m = b + (e - b) / 2; + index_t childl = 2 * node_index; + index_t childr = 2 * node_index + 1; + return std::max( + max_node_index(childl, b, m), + max_node_index(childr, m, e) + ); + } + + + void make_empty_box(Box& B) { + for(coord_index_t coord = 0; coord < 3; ++coord) { + B.xyz_min[coord] = 1.0; + B.xyz_max[coord] = 0.0; + } + } + + bool box_is_empty(Box& B) { + return (B.xyz_max[0] < B.xyz_min[0]); + } + + Mesh* the_mesh = nullptr; + + /** + * \brief Computes the hierarchy of bounding boxes recursively. + * \details This function is generic and can be used to compute + * a bbox hierarchy of arbitrary elements. + * \param[in] M the mesh + * \param[in] bboxes the array of bounding boxes + * \param[in] node_index the index of the root of the subtree + * \param[in] b first element index in the subtree + * \param[in] e one position past the last element index in the subtree + * \param[in] get_bbox a function that computes the bbox of an element + * \tparam GET_BBOX a function (or a functor) with the following arguments: + * - mesh: a const reference to the mesh + * - box: a reference where the computed bounding box of the element + * will be stored + * - element: the index of the element + */ + template + void init_bboxes_recursive( + const Mesh& M, vector& bboxes, + index_t node_index, + index_t b, index_t e, + const GET_BBOX& get_bbox + ) { + // DEBUGGING + if(node_index == 1) { + for(index_t i=0; i B.xyz_max[c]) { + inside = false; + result += geo_sqr(p[c] - B.xyz_max[c]); + } + } + if(inside) { + result = -inner_point_box_squared_distance(p, B); + } + return result; + } + + /** + * \brief Computes the squared distance between a point and the + * center of a box. + * \param[in] p the point + * \param[in] B the box + * \return the squared distance between \p p and the center of \p B + */ + double point_box_center_squared_distance( + const vec3& p, const Box& B + ) { + double result = 0.0; + for(coord_index_t c = 0; c < 3; ++c) { + double d = p[c] - 0.5 * (B.xyz_min[c] + B.xyz_max[c]); + result += geo_sqr(d); + } + return result; + } + + /** + * \brief Tests whether a mesh tetrahedron contains a given point + * \param[in] M a const reference to the mesh + * \param[in] t the index of the tetrahedron in \p M + * \param[in] p a const reference to the point + * \param[in] exact specifies whether exact predicates should be used + * \retval true if the tetrahedron \p t or its boundary contains + * the point \p p + * \retval false otherwise + */ + bool mesh_tet_contains_point( + const Mesh& M, index_t t, const vec3& p, bool exact = true + ) { + // Inexact mode is not implemented yet. + geo_argused(exact); + + const vec3& p0 = Geom::mesh_vertex(M, M.cells.vertex(t,0)); + const vec3& p1 = Geom::mesh_vertex(M, M.cells.vertex(t,1)); + const vec3& p2 = Geom::mesh_vertex(M, M.cells.vertex(t,2)); + const vec3& p3 = Geom::mesh_vertex(M, M.cells.vertex(t,3)); + + Sign s[4]; + s[0] = PCK::orient_3d(p, p1, p2, p3); + s[1] = PCK::orient_3d(p0, p, p2, p3); + s[2] = PCK::orient_3d(p0, p1, p, p3); + s[3] = PCK::orient_3d(p0, p1, p2, p); + + return ( + (s[0] >= 0 && s[1] >= 0 && s[2] >= 0 && s[3] >= 0) || + (s[0] <= 0 && s[1] <= 0 && s[2] <= 0 && s[3] <= 0) + ); + } + + + /** + * \brief Tests whether a segment intersects a triangle. + * \param[in] q1 , q2 the two extremities of the segment. + * \param[in] p1 , p2 , p3 the three vertices of the triangle. + * \retval true if [q1,q2] has an intersection with (p1, p2, p3). + * \retval false otherwise. + */ + bool segment_triangle_intersection( + const vec3& q1, const vec3& q2, + const vec3& p1, const vec3& p2, const vec3& p3 + ) { + + // If the segment does not straddle the supporting plane of the + // triangle, then there is no intersection. + vec3 N = cross(p2-p1, p3-p1); + if(dot(q1-p1,N)*dot(q2-p1,N) > 0.0) { + return false; + } + + // The three tetrahedra formed by the segment and the three edges + // of the triangle should have the same sign, else there is no + // intersection. + int s1 = geo_sgn(Geom::tetra_signed_volume(q1,q2,p1,p2)); + int s2 = geo_sgn(Geom::tetra_signed_volume(q1,q2,p2,p3)); + if(s1 != s2) { + return false; + } + int s3 = geo_sgn(Geom::tetra_signed_volume(q1,q2,p3,p1)); + return (s2 == s3); + } + + /** + * \brief Tests whether a segment intersects a triangle and has + * an intersection nearer than the intersection computed so far. + * \param[in] q1 , q2 the two extremities of the segment. + * \param[in] p1 , p2 , p3 the three vertices of the triangle. + * \param[in,out] nearest_t coordinate along [q1,q2] of the nearest + * intersection computed so far. + * \retval true if [q1,q2] has an intersection with (p1, p2, p3) that + * is nearer than the intersection computed so far. + * \retval false otherwise. + */ + bool segment_triangle_nearest_intersection( + const vec3& q1, const vec3& q2, + const vec3& p1, const vec3& p2, const vec3& p3, + double& nearest_t + ) { + if(!segment_triangle_intersection(q1,q2,p1,p2,p3)) { + return false; + } + + + // TODO: there is probably simpler/faster + // for instance: + // | p1 p2 p3 (tq2 + (1-t)q1) | + // | 1 1 1 1 | = 0 + + + // Normal vector of the triangle + + vec3 N = cross(p2-p1,p3-p1); + + // Plane equation: dot(N,p) = dot(N,p1) + // dot(q1 + t(q2-q1),N) = dot(p1, N) + // dot(q1,N) + t dot(q2-q1,N) = dot(p1,N) + // t = (dot(p1,N) - dot(q1,N)) / (dot(q2,N) - dot(q1,N)) + + double denom = dot(q2,N) - dot(q1,N); + if(::fabs(denom) < 1e-20) { + return false; + } + + double t = (dot(p1,N) - dot(q1,N)) / denom; + + + if(t < 0.0 || t > 1.0 || t > nearest_t) { + return false; + } + nearest_t = t; + return true; + } + + + /** + * \brief Tests whether there is an intersection between a segment + * and a mesh facet. + * \param[in] q1 , q2 the extremities of the segment + * \param[in] M the mesh + * \param[in] f the facet + */ + bool segment_mesh_facet_intersection( + const vec3& q1, const vec3& q2, + const Mesh& M, + index_t f + ) { + index_t c = M.facets.corners_begin(f); + const vec3& p1 = Geom::mesh_vertex(M, M.facet_corners.vertex(c)); + ++c; + while(c+1 != M.facets.corners_end(f)) { + const vec3& p2 = Geom::mesh_vertex(M, M.facet_corners.vertex(c)); + const vec3& p3 = Geom::mesh_vertex(M, M.facet_corners.vertex(c+1)); + if(segment_triangle_intersection(q1, q2, p1, p2, p3)) { + return true; + } + ++c; + } + return false; + } + + /** + * \brief Tests whether there is an intersection between a segment + * and a mesh facet. + * \param[in] q1 , q2 the extremities of the segment + * \param[in] M the mesh + * \param[in] f the facet + * \param[in,out] nearest_t coordinate along [q1,q2] of the + * nearest intersection so far + * \param[in,out] nearest_f index of the nearest intersected + * facet so far + */ + bool segment_mesh_facet_nearest_intersection( + const vec3& q1, const vec3& q2, + const Mesh& M, index_t f, + double& nearest_t, index_t& nearest_f + ) { + index_t c = M.facets.corners_begin(f); + const vec3& p1 = Geom::mesh_vertex(M, M.facet_corners.vertex(c)); + ++c; + while(c+1 != M.facets.corners_end(f)) { + const vec3& p2 = Geom::mesh_vertex(M, M.facet_corners.vertex(c)); + const vec3& p3 = Geom::mesh_vertex(M, M.facet_corners.vertex(c+1)); + if( + segment_triangle_nearest_intersection( + q1, q2, p1, p2, p3, nearest_t + ) + ) { + nearest_f = f; + return true; + } + ++c; + } + return false; + } + + inline double max3(double x1, double x2, double x3) { + return std::max(x1,std::max(x2,x3)); + } + + inline double min3(double x1, double x2, double x3) { + return std::min(x1,std::min(x2,x3)); + } + + + // https://tavianator.com/fast-branchless-raybounding-box-intersections/ + // https://tavianator.com/fast-branchless-raybounding-box-intersections-part-2-nans/ + // http://www.flipcode.com/archives/SSE_RayBox_Intersection_Test.shtml + // http://psgraphics.blogspot.com/2016/02/ray-box-intersection-and-fmin.html + + /** + * \brief Tests whether a segment intersects a box. + * \param[in] q1 the first extremity of the segment. + * \param[in] dirinv precomputed 1/(q2.x-q1.x), 1/(q2.y-q1.y), 1/(q2.z-q1.z) + * where q2 denotes the second extremity of the segment. + * \param[in] box the box. + * \retval true if [q1,q2] intersects the box. + * \retval false otherwise. + */ + bool segment_box_intersection( + const vec3& q1, const vec3& dirinv, const Box& box + ) { + // This version: slab method. + // Step 1: compute + // (tx1, tx2) : parameters of intersection with slab {xmin <= x <= xmax} + // (ty1, ty2) : parameters of intersection with slab {ymin <= y <= ymax} + // (tz1, tz2) : parameters of intersection with slab {zmin <= z <= zmax} + // (note: they are unordered, it is possible that tx1 > tx2) + // This defines three intervals: + // Ix = [ min(tx1,tx2) ... max(tx1,tx2) ] + // Iy = [ min(ty1,ty2) ... max(ty1,ty2) ] + // Iz = [ min(tz1,tz2) ... max(tz1,tz2) ] + // The intersection between [q1,q2] and the slab {xmin <= x <= xmax} is + // the set of points {q1 + t(q2-q1)} where t in Ix + + // Q: what does it do if one of the fracs is zero ? + // normally the tests with inf do what they should + // (to be tested) + + double tx1 = dirinv.x*(box.xyz_min[0] - q1.x); + double tx2 = dirinv.x*(box.xyz_max[0] - q1.x); + + double ty1 = dirinv.y*(box.xyz_min[1] - q1.y); + double ty2 = dirinv.y*(box.xyz_max[1] - q1.y); + + double tz1 = dirinv.z*(box.xyz_min[2] - q1.z); + double tz2 = dirinv.z*(box.xyz_max[2] - q1.z); + + // now compute the intersection of the three intervals + // Ix /\ Iy /\ Iz + // this gives us the range of t that corresponds to points in the + // box (because the box is the intersection of the 3 slabs) + // it starts at the maximum of the left bounds of the 3 intervals + // it stops at the minimum of the right bounds of the 3 intervals + + double tmin = + max3(std::min(tx1,tx2), std::min(ty1,ty2), std::min(tz1,tz2)); + + double tmax = + min3(std::max(tx1,tx2), std::max(ty1,ty2), std::max(tz1,tz2)); + + // There is no intersection if the interval is empty (tmin > tmax) + // or if the interval is outside [0,1] + + return (tmax >= 0.0) && (tmin < tmax) && (tmin <= 1.0); + } + + +} + +/****************************************************************************/ + +namespace GEO { + + MeshFacetsAABB::MeshFacetsAABB( + Mesh& M, bool reorder + ) : + mesh_(M) { + if(!M.facets.are_simplices()) { + mesh_repair( + M, MeshRepairMode(MESH_REPAIR_TRIANGULATE | MESH_REPAIR_QUIET) + ); + } + if(reorder) { + mesh_reorder(mesh_, MESH_ORDER_MORTON); + } + bboxes_.resize( + max_node_index( + 1, 0, mesh_.facets.nb() + ) + 1 // <-- this is because size == max_index + 1 !!! + ); + init_bboxes_recursive( + mesh_, bboxes_, 1, 0, mesh_.facets.nb(), get_facet_bbox + ); + } + + void MeshFacetsAABB::get_nearest_facet_hint( + const vec3& p, + index_t& nearest_f, vec3& nearest_point, double& sq_dist + ) const { + + // Find a good initial value for nearest_f by traversing + // the boxes and selecting the child such that the center + // of its bounding box is nearer to the query point. + // For a large mesh (20M facets) this gains up to 10% + // performance as compared to picking nearest_f randomly. + index_t b = 0; + index_t e = mesh_.facets.nb(); + index_t n = 1; + while(e != b + 1) { + index_t m = b + (e - b) / 2; + index_t childl = 2 * n; + index_t childr = 2 * n + 1; + if( + point_box_center_squared_distance(p, bboxes_[childl]) < + point_box_center_squared_distance(p, bboxes_[childr]) + ) { + e = m; + n = childl; + } else { + b = m; + n = childr; + } + } + nearest_f = b; + + index_t v = mesh_.facet_corners.vertex( + mesh_.facets.corners_begin(nearest_f) + ); + nearest_point = Geom::mesh_vertex(mesh_, v); + sq_dist = Geom::distance2(p, nearest_point); + } + + void MeshFacetsAABB::nearest_facet_recursive( + const vec3& p, + index_t& nearest_f, vec3& nearest_point, double& sq_dist, + index_t n, index_t b, index_t e + ) const { + geo_debug_assert(e > b); + + // If node is a leaf: compute point-facet distance + // and replace current if nearer + if(b + 1 == e) { + vec3 cur_nearest_point; + double cur_sq_dist; + get_point_facet_nearest_point( + mesh_, p, b, cur_nearest_point, cur_sq_dist + ); + if(cur_sq_dist < sq_dist) { + nearest_f = b; + nearest_point = cur_nearest_point; + sq_dist = cur_sq_dist; + } + return; + } + index_t m = b + (e - b) / 2; + index_t childl = 2 * n; + index_t childr = 2 * n + 1; + + double dl = point_box_signed_squared_distance(p, bboxes_[childl]); + double dr = point_box_signed_squared_distance(p, bboxes_[childr]); + + // Traverse the "nearest" child first, so that it has more chances + // to prune the traversal of the other child. + if(dl < dr) { + if(dl < sq_dist) { + nearest_facet_recursive( + p, + nearest_f, nearest_point, sq_dist, + childl, b, m + ); + } + if(dr < sq_dist) { + nearest_facet_recursive( + p, + nearest_f, nearest_point, sq_dist, + childr, m, e + ); + } + } else { + if(dr < sq_dist) { + nearest_facet_recursive( + p, + nearest_f, nearest_point, sq_dist, + childr, m, e + ); + } + if(dl < sq_dist) { + nearest_facet_recursive( + p, + nearest_f, nearest_point, sq_dist, + childl, b, m + ); + } + } + } + + + bool MeshFacetsAABB::segment_intersection(const vec3& q1, const vec3& q2) const { + vec3 dirinv( + 1.0/(q2.x-q1.x), + 1.0/(q2.y-q1.y), + 1.0/(q2.z-q1.z) + ); + return segment_intersection_recursive(q1, q2, dirinv, 1, 0, mesh_.facets.nb()); + } + + bool MeshFacetsAABB::segment_intersection_recursive( + const vec3& q1, const vec3& q2, const vec3& dirinv, index_t n, index_t b, index_t e + ) const { + if(!segment_box_intersection(q1, dirinv, bboxes_[n])) { + return false; + } + if(b + 1 == e) { + return segment_mesh_facet_intersection(q1, q2, mesh_, b); + } + index_t m = b + (e - b) / 2; + index_t childl = 2 * n; + index_t childr = 2 * n + 1; + return ( + segment_intersection_recursive(q1, q2, dirinv, childl, b, m) || + segment_intersection_recursive(q1, q2, dirinv, childr, m, e) + ); + } + + bool MeshFacetsAABB::segment_nearest_intersection( + const vec3& q1, const vec3& q2, double& t, index_t& f + ) const { + vec3 dirinv( + 1.0/(q2.x-q1.x), + 1.0/(q2.y-q1.y), + 1.0/(q2.z-q1.z) + ); + f = index_t(-1); + t = Numeric::max_float64(); + segment_nearest_intersection_recursive( + q1, q2, dirinv, 1, 0, mesh_.facets.nb(), t, f + ); + return (f != index_t(-1)); + } + + void MeshFacetsAABB::segment_nearest_intersection_recursive( + const vec3& q1, const vec3& q2, const vec3& dirinv, index_t n, index_t b, index_t e, + double& t, index_t& f + ) const { + if(!segment_box_intersection(q1, dirinv, bboxes_[n])) { + return; + } + if(b + 1 == e) { + segment_mesh_facet_nearest_intersection(q1, q2, mesh_, b, t, f); + return; + } + index_t m = b + (e - b) / 2; + index_t childl = 2 * n; + index_t childr = 2 * n + 1; + segment_nearest_intersection_recursive(q1, q2, dirinv, childl, b, m, t, f); + segment_nearest_intersection_recursive(q1, q2, dirinv, childr, m, e, t, f); + } + + +/****************************************************************************/ + + MeshCellsAABB::MeshCellsAABB(Mesh& M, bool reorder) : mesh_(M) { + debug = false; + if(reorder) { + mesh_reorder(mesh_, MESH_ORDER_MORTON); + } + bboxes_.resize( + max_node_index( + 1, 0, mesh_.cells.nb() + ) + 1 // <-- this is because size == max_index + 1 !!! + ); + if(mesh_.cells.are_simplices()) { + the_mesh = &mesh_; + init_bboxes_recursive( + mesh_, bboxes_, 1, 0, mesh_.cells.nb(), get_tet_bbox + ); + the_mesh = nullptr; + } else { + init_bboxes_recursive( + mesh_, bboxes_, 1, 0, mesh_.cells.nb(), get_cell_bbox + ); + } + } + + index_t MeshCellsAABB::containing_tet_recursive( + const vec3& p, bool exact, + index_t n, index_t b, index_t e + ) const { + if(debug) { + std::cerr << "Containing tet: " << b << " " << e << std::endl; + } + + if(!bboxes_[n].contains(p)) { + if(e==b+1) { + if(debug && mesh_tet_contains_point(mesh_, b, p, exact)) { + std::cerr << " INCOHERENCY !!" << std::endl; + std::cerr << "n=" << n << " b=" << b << std::endl; + + Box B; + get_tet_bbox(mesh_, B, b); + std::cerr + << "get bbox " + << B.xyz_min[0] << ' ' + << B.xyz_min[1] << ' ' + << B.xyz_min[2] << " - " + << B.xyz_max[0] << ' ' + << B.xyz_max[1] << ' ' + << B.xyz_max[2] + << std::endl; + B = bboxes_[n]; + std::cerr + << "bboxes[] " + << B.xyz_min[0] << ' ' + << B.xyz_min[1] << ' ' + << B.xyz_min[2] << " - " + << B.xyz_max[0] << ' ' + << B.xyz_max[1] << ' ' + << B.xyz_max[2] + << std::endl; + } + } + return NO_TET; + } + + if(e==b+1) { + if(debug) { + std::cerr << "Test " << b; + } + if(mesh_tet_contains_point(mesh_, b, p, exact)) { + if(debug) { + std::cerr << " contains" << std::endl; + } + return b; + } else { + if(debug) { + std::cerr << " does not contain" << std::endl; + } + return NO_TET; + } + } + + index_t m = b + (e - b) / 2; + index_t childl = 2 * n; + index_t childr = 2 * n + 1; + + index_t result = containing_tet_recursive( + p, exact, childl, b, m + ); + if(result == NO_TET) { + result = containing_tet_recursive(p, exact, childr, m, e); + } + return result; + } + +/****************************************************************************/ + +} + diff --git a/src/lib/geogram/mesh/mesh_AABB.h b/src/lib/geogram/mesh/mesh_AABB.h index ec971020..c378e345 100644 --- a/src/lib/geogram/mesh/mesh_AABB.h +++ b/src/lib/geogram/mesh/mesh_AABB.h @@ -151,6 +151,17 @@ namespace GEO { return nearest_facet; } + /** + * \brief Finds the nearest facet from an arbitrary 3d query point. + * \param[in] p query point + * \return the index of the facet nearest to point p. + */ + index_t nearest_facet(const vec3& p) const { + vec3 nearest_point; + double sq_dist; + return nearest_facet(p, nearest_point, sq_dist); + } + /** * \brief Computes the nearest point and nearest facet from * a query point, using user-specified hint. @@ -463,16 +474,15 @@ namespace GEO { /** * \brief Finds the index of a tetrahedron that contains a query point * \param[in] p a const reference to the query point - * \param[in] exact specifies whether exact predicates should be used * \return the index of one of the tetrahedra that contains \p p or * NO_TET if \p p is outside the mesh. * \note The input mesh needs to be tetrahedralized. If the mesh has * arbitrary cells, then one may use instead containing_boxes(). */ - index_t containing_tet(const vec3& p, bool exact =true) const { + index_t containing_tet(const vec3& p) const { geo_debug_assert(mesh_.cells.are_simplices()); return containing_tet_recursive( - p, exact, 1, 0, mesh_.cells.nb() + p, 1, 0, mesh_.cells.nb() ); } @@ -498,7 +508,21 @@ namespace GEO { ); } - + /** + * \brief Computes all the intersections between a given + * box and the bounding boxes of all the cells. + * \param[in] action a function that takes as argument + * an index_t (cell index) invoked for all cells that + * have a bounding box that intersects \p box_in. + */ + void compute_bbox_cell_bbox_intersections( + const Box& box_in, + std::function action + ) const { + bbox_intersect_recursive( + action, box_in, 1, 0, mesh_.cells.nb() + ); + } /** * \brief Finds all the cells such that their bounding @@ -569,12 +593,56 @@ namespace GEO { bbox_intersect_recursive(action, box, node_r, m, e); } + /** + * \brief Computes all the cells that have a bbox that + * intersects a given bbox in a sub-tree of the AABB tree. + * + * Note that the tree structure is completely implicit, + * therefore the bounds of the (continuous) facet indices + * sequences that correspond to the facets contained + * in the two nodes are sent as well as the node indices. + * + * \param[in] action a function that takes as argument + * an index_t (cell index) invoked for all cells that + * has a bounding box that overlaps \p box. + * \param[in] box the query box + * \param[in] node index of the first node of the AABB tree + * \param[in] b index of the first facet in \p node + * \param[in] e one position past the index of the last + * facet in \p node + */ + void bbox_intersect_recursive( + std::function action, + const Box& box, + index_t node, index_t b, index_t e + ) const { + geo_debug_assert(e != b); + + // Prune sub-tree that does not have intersection + if(!bboxes_overlap(box, bboxes_[node])) { + return; + } + + // Leaf case + if(e == b+1) { + action(b); + return; + } + + // Recursion + index_t m = b + (e - b) / 2; + index_t node_l = 2 * node; + index_t node_r = 2 * node + 1; + + bbox_intersect_recursive(action, box, node_l, b, m); + bbox_intersect_recursive(action, box, node_r, m, e); + } + /** * \brief The recursive function used by the implementation * of containing_tet(). * \param[in] p a const reference to the query point - * \param[in] exact specifies whether exact predicates should be used * \param[in] n index of the current node in the AABB tree * \param[in] b index of the first tet in the subtree under node \p n * \param[in] e one position past the index of the last tet in the @@ -583,7 +651,7 @@ namespace GEO { * NO_TET if \p p is outside the mesh. */ index_t containing_tet_recursive( - const vec3& p, bool exact, + const vec3& p, index_t n, index_t b, index_t e ) const; diff --git a/src/lib/geogram/mesh/mesh_AABB.h.debug b/src/lib/geogram/mesh/mesh_AABB.h.debug new file mode 100644 index 00000000..25091c56 --- /dev/null +++ b/src/lib/geogram/mesh/mesh_AABB.h.debug @@ -0,0 +1,656 @@ +/* + * Copyright (c) 2012-2014, Bruno Levy + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the ALICE Project-Team nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * If you modify this software, you should include a notice giving the + * name of the person performing the modification, the date of modification, + * and the reason for such modification. + * + * Contact: Bruno Levy + * + * Bruno.Levy@inria.fr + * http://www.loria.fr/~levy + * + * ALICE Project + * LORIA, INRIA Lorraine, + * Campus Scientifique, BP 239 + * 54506 VANDOEUVRE LES NANCY CEDEX + * FRANCE + * + */ + +#ifndef GEOGRAM_MESH_MESH_AABB +#define GEOGRAM_MESH_MESH_AABB + +/** + * \file mesh_AABB.h + * \brief Axis Aligned Bounding Box trees for accelerating + * geometric queries that operate on a Mesh. + */ + +#include +#include +#include + +namespace GEO { + + /** + * \brief Axis Aligned Bounding Box tree of mesh facets. + * \details Used to quickly compute facet intersection and + * to locate the nearest facet from 3d query points. + */ + class GEOGRAM_API MeshFacetsAABB { + public: + /** + * \brief Creates the Axis Aligned Bounding Boxes tree. + * \param[in] M the input mesh. It can be modified, + * and will be triangulated (if + * not already a triangular mesh). The facets are + * re-ordered (using Morton's order, see mesh_reorder()). + * \param[in] reorder if not set, Morton re-ordering is + * skipped (but it means that mesh_reorder() was previously + * called else the algorithm will be pretty unefficient). + * \pre M.facets.are_simplices() + */ + MeshFacetsAABB(Mesh& M, bool reorder = true); + + + /** + * \brief Gets the mesh. + * \return a const reference to the mesh. + */ + const Mesh& mesh() const { + return mesh_; + } + + /** + * \brief Computes all the pairs of intersecting facets. + * \param[in] action ACTION::operator(index_t,index_t) is + * invoked of all pairs of facets that have overlapping + * bounding boxes. triangles_intersection() needs to be + * called to detect the actual intersections. + * \tparam ACTION user action class, that needs to define + * operator(index_t,index_t), where the two indices are + * the indices each pair of triangles that have intersecting + * bounding boxes. + */ + template + void compute_facet_bbox_intersections( + ACTION& action + ) const { + intersect_recursive( + action, + 1, 0, mesh_.facets.nb(), + 1, 0, mesh_.facets.nb() + ); + } + + + /** + * \brief Computes all the intersections between a given + * box and the bounding boxes of all the facets. + * \param[in] action ACTION::operator(index_t) is + * invoked for all facets that have a bounding + * box that intersects \p box_in. + * \tparam ACTION user action class, that needs to define + * operator(index_t), where the parameter is the index + * of the triangle that has its bounding box intersecting + * \p box_in. + */ + template< class ACTION > + void compute_bbox_facet_bbox_intersections( + const Box& box_in, + ACTION& action + ) const { + bbox_intersect_recursive( + action, box_in, 1, 0, mesh_.facets.nb() + ); + } + + /** + * \brief Finds the nearest facet from an arbitrary 3d query point. + * \param[in] p query point + * \param[out] nearest_point nearest point on the surface + * \param[out] sq_dist squared distance between p and the surface. + * \return the index of the facet nearest to point p. + */ + index_t nearest_facet( + const vec3& p, vec3& nearest_point, double& sq_dist + ) const { + index_t nearest_facet; + get_nearest_facet_hint(p, nearest_facet, nearest_point, sq_dist); + nearest_facet_recursive( + p, + nearest_facet, nearest_point, sq_dist, + 1, 0, mesh_.facets.nb() + ); + return nearest_facet; + } + + /** + * \brief Finds the nearest facet from an arbitrary 3d query point. + * \param[in] p query point + * \return the index of the facet nearest to point p. + */ + index_t nearest_facet(const vec3& p) const { + vec3 nearest_point; + double sq_dist; + return nearest_facet(p, nearest_point, sq_dist); + } + + /** + * \brief Computes the nearest point and nearest facet from + * a query point, using user-specified hint. + * + * \details The hint is specified as reasonable initial values of + * (nearest_facet, nearest_point, sq_dist). If multiple queries + * are done on a set of points that has spatial locality, + * the hint can be the result of the previous call. + * + * \param[in] p query point + * \param[in,out] nearest_facet the nearest facet so far, + * or NO_FACET if not known yet + * \param[in,out] nearest_point a point in nearest_facet + * \param[in,out] sq_dist squared distance between p and + * nearest_point + * \note On entry, \p sq_dist needs to be equal to the squared + * distance between \p p and \p nearest_point (it is easy to + * forget to update it when calling it within a loop). + */ + void nearest_facet_with_hint( + const vec3& p, + index_t& nearest_facet, vec3& nearest_point, double& sq_dist + ) const { + if(nearest_facet == NO_FACET) { + get_nearest_facet_hint( + p, nearest_facet, nearest_point, sq_dist + ); + } + nearest_facet_recursive( + p, + nearest_facet, nearest_point, sq_dist, + 1, 0, mesh_.facets.nb() + ); + } + + /** + * \brief Computes the distance between an arbitrary 3d query + * point and the surface. + * \param[in] p query point + * \return the squared distance between \p p and the surface. + */ + double squared_distance(const vec3& p) const { + vec3 nearest_point; + double result; + nearest_facet(p, nearest_point, result); + return result; + } + + /** + * \brief Tests whether this surface mesh has an intersection + * with a segment. + * \param[in] q1 , q2 the two extremities of the segment. + * \retval true if there exists an intersection between [q1 , q2] + * and a facet of the mesh. + * \retval false otherwise. + */ + bool segment_intersection(const vec3& q1, const vec3& q2) const; + + + /** + * \brief Finds the intersection between a segment and a surface that + * is nearest to the first extremity of the segment. + * \param[in] q1 , q2 the two extremities of the segment. + * \param[out] t if there was an intersection, it is t*q2 + (1-t)*q1 + * \param[out] f the intersected nearest facet or index_t(-1) if there + * was no intersection. + * \retval true if there exists at least an intersection between [q1 , q2] + * and a facet of the mesh. + * \retval false otherwise. + */ + bool segment_nearest_intersection( + const vec3& q1, const vec3& q2, double& t, index_t& f + ) const; + + + protected: + + + /** + * \brief Computes all the facets that have a bbox that + * intersects a given bbox in a sub-tree of the AABB tree. + * + * Note that the tree structure is completely implicit, + * therefore the bounds of the (continuous) facet indices + * sequences that correspond to the facets contained + * in the two nodes are sent as well as the node indices. + * + * \param[in] action ACTION::operator(index_t) is + * invoked for all facet that has a bounding box that + * overlaps \p box. + * \param[in] node index of the first node of the AABB tree + * \param[in] b index of the first facet in \p node + * \param[in] e one position past the index of the last + * facet in \p node + */ + template + void bbox_intersect_recursive( + ACTION& action, + const Box& box, + index_t node, index_t b, index_t e + ) const { + geo_debug_assert(e != b); + + // Prune sub-tree that does not have intersection + if(!bboxes_overlap(box, bboxes_[node])) { + return; + } + + // Leaf case + if(e == b+1) { + action(b); + return; + } + + // Recursion + index_t m = b + (e - b) / 2; + index_t node_l = 2 * node; + index_t node_r = 2 * node + 1; + + bbox_intersect_recursive(action, box, node_l, b, m); + bbox_intersect_recursive(action, box, node_r, m, e); + } + + /** + * \brief Computes all the pairs of intersecting facets + * for two sub-trees of the AABB tree. + * + * Note that the tree structure is completely implicit, + * therefore the bounds of the (continuous) facet indices + * sequences that correspond to the facets contained + * in the two nodes are sent as well as the node indices. + * + * \param[in] action ACTION::operator(index_t,index_t) is + * invoked of all pairs of facets that have overlapping + * bounding boxes. + * \param[in] node1 index of the first node of the AABB tree + * \param[in] b1 index of the first facet in \p node1 + * \param[in] e1 one position past the index of the last + * facet in \p node1 + * \param[in] node2 index of the second node of the AABB tree + * \param[in] b2 index of the first facet in \p node2 + * \param[in] e2 one position past the index of the second + * facet in \p node2 + */ + template + void intersect_recursive( + ACTION& action, + index_t node1, index_t b1, index_t e1, + index_t node2, index_t b2, index_t e2 + ) const { + geo_debug_assert(e1 != b1); + geo_debug_assert(e2 != b2); + + // Since we are intersecting the AABBTree with *itself*, + // we can prune half of the cases by skipping the test + // whenever node2's facet index interval is greated than + // node1's facet index interval. + if(e2 <= b1) { + return; + } + + // The acceleration is here: + if(!bboxes_overlap(bboxes_[node1], bboxes_[node2])) { + return; + } + + // Simple case: leaf - leaf intersection. + if(b1 + 1 == e1 && b2 + 1 == e2) { + action(b1, b2); + return; + } + + // If node2 has more facets than node1, then + // intersect node2's two children with node1 + // else + // intersect node1's two children with node2 + if(e2 - b2 > e1 - b1) { + index_t m2 = b2 + (e2 - b2) / 2; + index_t node2_l = 2 * node2; + index_t node2_r = 2 * node2 + 1; + intersect_recursive(action, node1, b1, e1, node2_l, b2, m2); + intersect_recursive(action, node1, b1, e1, node2_r, m2, e2); + } else { + index_t m1 = b1 + (e1 - b1) / 2; + index_t node1_l = 2 * node1; + index_t node1_r = 2 * node1 + 1; + intersect_recursive(action, node1_l, b1, m1, node2, b2, e2); + intersect_recursive(action, node1_r, m1, e1, node2, b2, e2); + } + } + + /** + * \brief Computes a reasonable initialization for + * nearest facet search. + * + * \details A good initialization makes the algorithm faster, + * by allowing early pruning of subtrees that provably + * do not contain the nearest neighbor. + * + * \param[in] p query point + * \param[out] nearest_facet a facet reasonably near p + * \param[out] nearest_point a point in nearest_facet + * \param[out] sq_dist squared distance between p and nearest_point + */ + void get_nearest_facet_hint( + const vec3& p, + index_t& nearest_facet, vec3& nearest_point, double& sq_dist + ) const; + + /** + * \brief The recursive function used by the implementation + * of nearest_facet(). + * + * \details The first call may use get_nearest_facet_hint() + * to initialize nearest_facet, nearest_point and sq_dist, + * as done in nearest_facet(). + * + * \param[in] p query point + * \param[in,out] nearest_facet the nearest facet so far, + * \param[in,out] nearest_point a point in nearest_facet + * \param[in,out] sq_dist squared distance between p and nearest_point + * \param[in] n index of the current node in the AABB tree + * \param[in] b index of the first facet in the subtree under node \p n + * \param[in] e one position past the index of the last facet in the + * subtree under node \p n + */ + void nearest_facet_recursive( + const vec3& p, + index_t& nearest_facet, vec3& nearest_point, double& sq_dist, + index_t n, index_t b, index_t e + ) const; + + /** + * \brief The recursive function used by the implementation + * of segment_intersection() + * \param[in] q1 , q2 the segment + * \param[in] dirinv precomputed 1/(q2.x-q1.x), 1/(q2.y-q1.y), 1/(q2.z-q1.z) + * \param[in] n index of the current node in the AABB tree + * \param[in] b index of the first facet in the subtree under node \p n + * \param[in] e one position past the index of the last facet in the + * subtree under node \p n + * \retval true if their was an intersection + * \retval false otherwise + */ + bool segment_intersection_recursive( + const vec3& q1, const vec3& q2, const vec3& dirinv, + index_t n, index_t b, index_t e + ) const; + + /** + * \brief The recursive function used by the implementation + * of segment_nearest_intersection() + * \param[in] q1 , q2 the segment + * \param[in] dirinv precomputed 1/(q2.x-q1.x), 1/(q2.y-q1.y), 1/(q2.z-q1.z) + * \param[in] n index of the current node in the AABB tree + * \param[in] b index of the first facet in the subtree under node \p n + * \param[in] e one position past the index of the last facet in the + * subtree under node \p n + * \param[in,out] t the coordinate along [q1,q2] of the nearest + * intersection so-far. + * \param[in,out] f the nearest intersected facet so-far. + */ + void segment_nearest_intersection_recursive( + const vec3& q1, const vec3& q2, const vec3& dirinv, + index_t n, index_t b, index_t e, + double& t, index_t& f + ) const; + + + protected: + vector bboxes_; + Mesh& mesh_; + }; + + /***********************************************************************/ + + /** + * \brief Axis Aligned Bounding Box tree of mesh tetrahedra. + * \details Used to quickly find the tetrahedron that contains + * a given 3d point. + */ + class GEOGRAM_API MeshCellsAABB { + public: + bool debug; + + /** + * \brief Symbolic constant for indicating that there + * is no containing tetrahedron. + * \see containing_tet() + */ + static const index_t NO_TET = index_t(-1); + + /** + * \brief Creates the Axis Aligned Bounding Boxes tree. + * \param[in] M the input mesh. It can be modified, + * The cells are re-ordered (using Morton's order, see mesh_reorder()). + * \param[in] reorder if not set, Morton re-ordering is + * skipped (but it means that mesh_reorder() was previously + * called else the algorithm will be pretty unefficient). + */ + MeshCellsAABB(Mesh& M, bool reorder = true); + + /** + * \brief Gets the mesh. + * \return a const reference to the mesh. + */ + const Mesh& mesh() const { + return mesh_; + } + + /** + * \brief Finds the index of a tetrahedron that contains a query point + * \param[in] p a const reference to the query point + * \param[in] exact specifies whether exact predicates should be used + * \return the index of one of the tetrahedra that contains \p p or + * NO_TET if \p p is outside the mesh. + * \note The input mesh needs to be tetrahedralized. If the mesh has + * arbitrary cells, then one may use instead containing_boxes(). + */ + index_t containing_tet(const vec3& p, bool exact =true) const { + geo_debug_assert(mesh_.cells.are_simplices()); + return containing_tet_recursive( + p, exact, 1, 0, mesh_.cells.nb() + ); + } + + + /** + * \brief Computes all the intersections between a given + * box and the bounding boxes of all the cells. + * \param[in] action ACTION::operator(index_t) is + * invoked for all cells that have a bounding + * box that intersects \p box_in. + * \tparam ACTION user action class, that needs to define + * operator(index_t), where the parameter is the index + * of the cell that has its bounding box intersecting + * \p box_in. + */ + template< class ACTION > + void compute_bbox_cell_bbox_intersections( + const Box& box_in, + ACTION& action + ) const { + bbox_intersect_recursive( + action, box_in, 1, 0, mesh_.cells.nb() + ); + } + + + + /** + * \brief Finds all the cells such that their bounding + * box contain a point. + * \param[in] action ACTION::operator(index_t) is + * invoked for all cells that have a bounding + * box that contains \p p. + * \tparam ACTION user action class, that needs to define + * operator(index_t), where the parameter is the index + * of the cell that has its bounding box containing + * \p p. + */ + template< class ACTION > + void containing_boxes( + const vec3& p, + ACTION& action + ) const { + containing_boxes_recursive( + action, p, 1, 0, mesh_.cells.nb() + ); + } + + protected: + + /** + * \brief Computes all the cells that have a bbox that + * intersects a given bbox in a sub-tree of the AABB tree. + * + * Note that the tree structure is completely implicit, + * therefore the bounds of the (continuous) facet indices + * sequences that correspond to the facets contained + * in the two nodes are sent as well as the node indices. + * + * \param[in] action ACTION::operator(index_t) is + * invoked for all cells that has a bounding box that + * overlaps \p box. + * \param[in] box the query box + * \param[in] node index of the first node of the AABB tree + * \param[in] b index of the first facet in \p node + * \param[in] e one position past the index of the last + * facet in \p node + */ + template + void bbox_intersect_recursive( + ACTION& action, + const Box& box, + index_t node, index_t b, index_t e + ) const { + geo_debug_assert(e != b); + + // Prune sub-tree that does not have intersection + if(!bboxes_overlap(box, bboxes_[node])) { + return; + } + + // Leaf case + if(e == b+1) { + action(b); + return; + } + + // Recursion + index_t m = b + (e - b) / 2; + index_t node_l = 2 * node; + index_t node_r = 2 * node + 1; + + bbox_intersect_recursive(action, box, node_l, b, m); + bbox_intersect_recursive(action, box, node_r, m, e); + } + + + /** + * \brief The recursive function used by the implementation + * of containing_tet(). + * \param[in] p a const reference to the query point + * \param[in] exact specifies whether exact predicates should be used + * \param[in] n index of the current node in the AABB tree + * \param[in] b index of the first tet in the subtree under node \p n + * \param[in] e one position past the index of the last tet in the + * subtree under node \p n + * \return the index of one of the tetrahedra that contains \p p, or + * NO_TET if \p p is outside the mesh. + */ + index_t containing_tet_recursive( + const vec3& p, bool exact, + index_t n, index_t b, index_t e + ) const; + + + /** + * \brief Computes all the cells that have a bbox that + * contain a given point in a sub-tree of the AABB tree. + * + * Note that the tree structure is completely implicit, + * therefore the bounds of the (continuous) facet indices + * sequences that correspond to the facets contained + * in the two nodes are sent as well as the node indices. + * + * \param[in] action ACTION::operator(index_t) is + * invoked for all cells that has a bounding box that + * contains \p p. + * \param[in] p a const reference to the query point + * \param[in] node index of the first node of the AABB tree + * \param[in] b index of the first facet in \p node + * \param[in] e one position past the index of the last + * facet in \p node + */ + template + void containing_bboxes_recursive( + ACTION& action, + const vec3& p, + index_t node, index_t b, index_t e + ) const { + geo_debug_assert(e != b); + + // Prune sub-tree that does not have intersection + if(!bboxes_[node].contains(p)) { + return; + } + + // Leaf case + if(e == b+1) { + action(b); + return; + } + + // Recursion + index_t m = b + (e - b) / 2; + index_t node_l = 2 * node; + index_t node_r = 2 * node + 1; + + containing_bbox_recursive(action, p, node_l, b, m); + containing_bbox_recursive(action, p, node_r, m, e); + } + + vector bboxes_; + Mesh& mesh_; + }; + +} + +#endif + diff --git a/src/lib/geogram/mesh/mesh_reorder.cpp b/src/lib/geogram/mesh/mesh_reorder.cpp index 349d962b..39d18669 100644 --- a/src/lib/geogram/mesh/mesh_reorder.cpp +++ b/src/lib/geogram/mesh/mesh_reorder.cpp @@ -791,7 +791,7 @@ namespace { ) : M_(M) { - geo_debug_assert(e > b); + geo_debug_assert(e >= b); geo_cite_with_info( "WEB:SpatialSorting", "The implementation of spatial sort in GEOGRAM is inspired by " @@ -1470,7 +1470,7 @@ namespace GEO { //The next three lines replace the following commented-out line //(random_shuffle is deprecated in C++17, and they call this - // progess...) + // progress...) // std::random_shuffle(b,e); std::random_device rng; std::mt19937 urng(rng()); diff --git a/src/lib/geogram/numerics/Predicates.psm b/src/lib/geogram/numerics/Predicates.psm index 66e38f3f..a0e6beb3 100644 --- a/src/lib/geogram/numerics/Predicates.psm +++ b/src/lib/geogram/numerics/Predicates.psm @@ -24,6 +24,7 @@ predicates/side4h.h \ predicates/orient2d.h \ predicates/orient3d.h \ predicates/dot3d.h \ +predicates/dot_compare_3d.h \ predicates/det3d.h \ predicates/det4d.h \ predicates/aligned3d.h \ diff --git a/src/lib/geogram/numerics/predicates.cpp b/src/lib/geogram/numerics/predicates.cpp index 09166815..65c74de8 100644 --- a/src/lib/geogram/numerics/predicates.cpp +++ b/src/lib/geogram/numerics/predicates.cpp @@ -74,6 +74,7 @@ #include #include #include +#include #include #ifdef __SSE2__ @@ -1812,6 +1813,31 @@ namespace { return Delta.sign(); } + + /** + * \brief Compares two dot products using exact arithmetics. + * \param[in] v0 , v1 , v2 three vectors + * \return the sign of v0.v1 - v0.v2 + */ + Sign dot_compare_3d_exact( + const double* v0, const double* v1, const double* v2 + ) { + const expansion& d01_0 = expansion_product(v0[0], v1[0]); + const expansion& d01_1 = expansion_product(v0[1], v1[1]); + const expansion& d01_2 = expansion_product(v0[2], v1[2]); + const expansion& d01_12 = expansion_sum(d01_1, d01_2); + const expansion& d01 = expansion_sum(d01_0, d01_12); + + const expansion& d02_0 = expansion_product(v0[0], v2[0]); + const expansion& d02_1 = expansion_product(v0[1], v2[1]); + const expansion& d02_2 = expansion_product(v0[2], v2[2]); + const expansion& d02_12 = expansion_sum(d02_1, d02_2); + const expansion& d02 = expansion_sum(d02_0, d02_12); + + const expansion& result = expansion_diff(d01, d02); + + return result.sign(); + } // ================================ statistics ======================== @@ -2323,15 +2349,24 @@ namespace GEO { Sign dot_3d( const double* p0, const double* p1, const double* p2 ) { - Sign result = Sign( - det_3d_filter(p0, p1, p2) - ); + Sign result = Sign(det_3d_filter(p0, p1, p2)); if(result == 0) { result = dot_3d_exact(p0, p1, p2); } return result; } + Sign dot_compare_3d( + const double* v0, const double* v1, const double* v2 + ) { + Sign result = Sign(dot_compare_3d_filter(v0, v1, v2)); + if(result == 0) { + result = dot_compare_3d_exact(v0, v1, v2); + } + return result; + } + + bool points_are_identical_2d( const double* p1, const double* p2 diff --git a/src/lib/geogram/numerics/predicates.h b/src/lib/geogram/numerics/predicates.h index 2e4d5e28..1e6c8a73 100644 --- a/src/lib/geogram/numerics/predicates.h +++ b/src/lib/geogram/numerics/predicates.h @@ -68,7 +68,7 @@ namespace GEO { /** * \brief Mode for symbolic perturbations. */ - enum SOSMode {SOS_ADDRESS, SOS_LEXICO }; + enum SOSMode { SOS_ADDRESS, SOS_LEXICO }; /** * \brief Sets the current mode for handling symbolic perturbations @@ -588,6 +588,15 @@ namespace GEO { const double* p0, const double* p1, const double* p2 ); + /** + * \brief Compares two dot products. + * \param[in] v0 , v1 , v2 three vectors. + * \return the sign of v0.v1 - v0.v2 + */ + Sign GEOGRAM_API dot_compare_3d( + const double* v0, const double* v1, const double* v2 + ); + /** * \brief Tests whether two 2d points are identical. * \param[in] p1 first point diff --git a/src/lib/geogram/numerics/predicates/dot_compare_3d.h b/src/lib/geogram/numerics/predicates/dot_compare_3d.h new file mode 100644 index 00000000..a46bf77a --- /dev/null +++ b/src/lib/geogram/numerics/predicates/dot_compare_3d.h @@ -0,0 +1,87 @@ +/* Automatically generated code, do not edit */ +/* Generated from source file: dot_compare_3d.pck */ + +inline int dot_compare_3d_filter( const double* p0, const double* p1, const double* p2) { + double d1; + d1 = (((p0[0] * p1[0]) + (p0[1] * p1[1])) + (p0[2] * p1[2])); + double d2; + d2 = (((p0[0] * p2[0]) + (p0[1] * p2[1])) + (p0[2] * p2[2])); + int int_tmp_result; + double double_tmp_result; + double eps; + double_tmp_result = (d1 - d2); + double max1 = fabs(p0[0]); + if( (max1 < fabs(p0[1])) ) + { + max1 = fabs(p0[1]); + } + if( (max1 < fabs(p0[2])) ) + { + max1 = fabs(p0[2]); + } + double max2 = fabs(p1[0]); + if( (max2 < fabs(p1[1])) ) + { + max2 = fabs(p1[1]); + } + if( (max2 < fabs(p1[2])) ) + { + max2 = fabs(p1[2]); + } + if( (max2 < fabs(p2[0])) ) + { + max2 = fabs(p2[0]); + } + if( (max2 < fabs(p2[1])) ) + { + max2 = fabs(p2[1]); + } + if( (max2 < fabs(p2[2])) ) + { + max2 = fabs(p2[2]); + } + double lower_bound_1; + double upper_bound_1; + lower_bound_1 = max1; + upper_bound_1 = max1; + if( (max2 < lower_bound_1) ) + { + lower_bound_1 = max2; + } + else + { + if( (max2 > upper_bound_1) ) + { + upper_bound_1 = max2; + } + } + if( (lower_bound_1 < 3.01698158319050667656e-147) ) + { + return FPG_UNCERTAIN_VALUE; + } + else + { + if( (upper_bound_1 > 1.67597599124282407923e+153) ) + { + return FPG_UNCERTAIN_VALUE; + } + eps = (2.44455106181954323552e-15 * (max1 * max2)); + if( (double_tmp_result > eps) ) + { + int_tmp_result = 1; + } + else + { + if( (double_tmp_result < -eps) ) + { + int_tmp_result = -1; + } + else + { + return FPG_UNCERTAIN_VALUE; + } + } + } + return int_tmp_result; +} + diff --git a/src/lib/geogram/numerics/predicates/dot_compare_3d.pck b/src/lib/geogram/numerics/predicates/dot_compare_3d.pck new file mode 100644 index 00000000..0ec8f2aa --- /dev/null +++ b/src/lib/geogram/numerics/predicates/dot_compare_3d.pck @@ -0,0 +1,13 @@ +#include "kernel.pckh" + +#ifdef DIM3 + +Sign predicate(dot_compare)( + point(p0), point(p1), point(p2) DIM +) { + scalar d1 = p0_0*p1_0 + p0_1*p1_1 + p0_2 * p1_2; + scalar d2 = p0_0*p2_0 + p0_1*p2_1 + p0_2 * p2_2; + return sign(d1-d2); +} + +#endif diff --git a/src/lib/geogram/voronoi/RVD.cpp.new b/src/lib/geogram/voronoi/RVD.cpp.new deleted file mode 100644 index d38c0ccb..00000000 --- a/src/lib/geogram/voronoi/RVD.cpp.new +++ /dev/null @@ -1,2651 +0,0 @@ -/* - * Copyright (c) 2012-2014, Bruno Levy - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * * Neither the name of the ALICE Project-Team nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * If you modify this software, you should include a notice giving the - * name of the person performing the modification, the date of modification, - * and the reason for such modification. - * - * Contact: Bruno Levy - * - * Bruno.Levy@inria.fr - * http://www.loria.fr/~levy - * - * ALICE Project - * LORIA, INRIA Lorraine, - * Campus Scientifique, BP 239 - * 54506 VANDOEUVRE LES NANCY CEDEX - * FRANCE - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * There are three levels of implementation: - * Level 1: RestrictedVoronoiDiagram is the abstract API seen from client code - * Level 2: RVD_Nd_Impl implements RestrictedVoronoiDiagram - * Level 3: RVD_Nd_Impl::GenRestrictedVoronoiDiagram is - * an instantiation of GEOGen::RestrictedVoronoiDiagram (from generic_RVD.h) - * - * Warning: there are approx. 1000 lines of boring code ahead. - */ - -namespace { - - using namespace GEO; - - /** - * \brief Generic implementation of RestrictedVoronoiDiagram. - * \tparam DIM dimension - */ - template - class RVD_Nd_Impl : public GEO::RestrictedVoronoiDiagram { - - /** \brief This class type */ - typedef RVD_Nd_Impl thisclass; - - /** \brief The base class of this class */ - typedef RestrictedVoronoiDiagram baseclass; - - public: - /** \brief Implementation based on the generic version. */ - typedef GEOGen::RestrictedVoronoiDiagram - GenRestrictedVoronoiDiagram; - - /** \brief Representation of points. */ - typedef vecng Point; - - /** \brief Representation of vectors. */ - typedef vecng Vector; - - /** \brief Represents a point and its symbolic information. */ - typedef typename GenRestrictedVoronoiDiagram::Vertex Vertex; - - /** - * \brief Specifies the computation done by the threads. - */ - enum ThreadMode { - MT_NONE, /**< uninitialized */ - MT_LLOYD, /**< Lloyd iteration */ - MT_NEWTON, /**< Newton optimization */ - MT_INT_SMPLX, /**< Newton with integration simplex */ - MT_POLYG, /**< Polygon callback */ - MT_POLYH /**< Polyhedron callback */ - }; - - /** - * \brief Gets a mesh vertex from its index. - * \param[in] v index of the vertex - * \return a const reference to a Point - */ - const Point& mesh_vertex(index_t v) { - return *(const Point*) mesh_->vertices.point_ptr(v); - } - - - /** - * \brief Creates a RVD_Nd_Impl. - * - * \details The dimension is determined by \p mesh->dimension(). - * \param[in] delaunay the Delaunay triangulation - * \param[in] mesh the input mesh - * \param[in] R3_embedding gives for each vertex - * its mapping in 3D space. - * \param[in] R3_embedding_stride gives the stride between - * two consecutive vertices in R3_embedding - */ - RVD_Nd_Impl( - Delaunay* delaunay, Mesh* mesh, - const double* R3_embedding, index_t R3_embedding_stride - ) : - RestrictedVoronoiDiagram( - delaunay, mesh, R3_embedding, R3_embedding_stride - ), - RVD_(delaunay, mesh) { - use_exact_projection_ = false; - is_slave_ = false; - master_ = nullptr; - has_weights_ = false; - if(mesh->vertices.attributes().is_defined("weight")) { - vertex_weight_.bind(mesh->vertices.attributes(), "weight"); - has_weights_ = true; - } - parts_ = nullptr; - nb_parts_ = 0; - funcval_ = 0.0; - simplex_func_ = nullptr; - polygon_callback_ = nullptr; - polyhedron_callback_ = nullptr; - arg_vectors_ = nullptr; - arg_scalars_ = nullptr; - thread_mode_ = MT_NONE; - nb_triangles_ = 0; - } - - /** - * \brief Constructor for parts, used in multithreading mode. - */ - RVD_Nd_Impl() : - RestrictedVoronoiDiagram(nullptr, nullptr, nullptr, 0), - RVD_(nullptr, nullptr) { - use_exact_projection_ = false; - is_slave_ = true; - master_ = nullptr; - mesh_ = nullptr; - parts_ = nullptr; - nb_parts_ = 0; - facets_begin_ = -1; - facets_end_ = -1; - funcval_ = 0.0; - simplex_func_ = nullptr; - polygon_callback_ = nullptr; - polyhedron_callback_ = nullptr; - arg_vectors_ = nullptr; - arg_scalars_ = nullptr; - thread_mode_ = MT_NONE; - nb_triangles_ = 0; - } - - void set_delaunay(Delaunay* delaunay) override { - baseclass::set_delaunay(delaunay); - RVD_.set_delaunay(delaunay); - for(index_t p = 0; p < nb_parts_; ++p) { - parts_[p].set_delaunay(delaunay); - } - } - - void set_check_SR(bool x) override { - RVD_.set_check_SR(x); - for(index_t p = 0; p < nb_parts_; ++p) { - parts_[p].set_check_SR(x); - } - } - - void set_exact_predicates(bool x) override { - RVD_.set_exact_predicates(x); - for(index_t p = 0; p < nb_parts_; ++p) { - parts_[p].set_exact_predicates(x); - } - } - - bool exact_predicates() const override { - return RVD_.exact_predicates(); - } - - /********************************************************************/ - - /** - * \brief Place holder, "no locking" policy. - * \details NoLocks is used by algorithms templated - * by locking policy, for the single-threaded instances - * that do not need synchronization. The multi-threaded - * instances are parameterized by SpinLockArray. - */ - class NoLocks { - public: - /** - * \brief Acquires a spinlock. - * \details Does nothing in this version - * \param[in] i index of the spinlock to acquire - */ - void acquire_spinlock(index_t i) { - geo_argused(i); - } - - /** - * \brief Releases a spinlock. - * \details Does nothing in this version - * \param[in] i index of the spinlock to release - */ - void release_spinlock(index_t i) { - geo_argused(i); - } - }; - - // ____________________________________________________________________ - - /** - * \brief Implementation class for surfacic Lloyd relaxation. - * \details To be used as a template argument - * to RVD::for_each_triangle(). - * This version ignores the weights. - * - * Computes for each RVD cell: - * - mg[v] (v's Voronoi cell's total area times centroid) - * - m[v] (v's total area) - * \tparam LOCKS locking policy - * (can be one of Process::SpinLockArray, NoLocks) - */ - template - class ComputeCentroids { - public: - /** - * \brief Constructs a ComputeCentroids. - * \param[out] mg where to store the centroids - * \param[out] m where to store the masses - * \param[in] locks the array of locks - * (or NoLocks in single thread mode) - */ - ComputeCentroids( - double* mg, - double* m, - LOCKS& locks - ) : - mg_(mg), - m_(m), - locks_(locks) { - } - - /** - * \brief The callback called for each integration simplex. - * \param[in] v index of current center vertex - * \param[in] p1 first vertex of current integration simplex - * \param[in] p2 second vertex of current integration simplex - * \param[in] p3 third vertex of current integration simplex - */ - void operator() ( - index_t v, - const double* p1, - const double* p2, - const double* p3 - ) const { - double cur_m = Geom::triangle_area(p1, p2, p3, DIM); - double s = cur_m / 3.0; - locks_.acquire_spinlock(v); - m_[v] += cur_m; - double* cur_mg_out = mg_ + v * DIM; - for(coord_index_t coord = 0; coord < DIM; coord++) { - cur_mg_out[coord] += - s * (p1[coord] + p2[coord] + p3[coord]); - } - locks_.release_spinlock(v); - } - - private: - double* mg_; - double* m_; - LOCKS& locks_; - }; - - /** - * \brief Implementation class for surfacic Lloyd relaxation. - * \details To be used as a template - * argument to RVD::for_each_triangle(). - * This version takes the weights into account. - * - * Computes for each RVD cell: - * - mg[v] (v's Voronoi cell's total area times centroid) - * - m[v] (v's total area) - * \tparam LOCKS locking policy - * (can be one of Process::SpinLockArray, NoLocks) - */ - template - class ComputeCentroidsWeighted { - public: - /** - * \brief Constructs a ComputeCentroidsWeighted. - * \param[out] mg where to store the centroids - * \param[out] m where to store the masses - * \param[in] locks the array of locks - * (or NoLocks in single thread mode) - */ - ComputeCentroidsWeighted( - double* mg, - double* m, - LOCKS& locks - ) : - mg_(mg), - m_(m), - locks_(locks) { - } - - /** - * \brief The callback called for each integration simplex. - * \param[in] v index of current center vertex - * \param[in] v1 first vertex of current integration simplex - * \param[in] v2 second vertex of current integration simplex - * \param[in] v3 third vertex of current integration simplex - */ - void operator() ( - index_t v, - const Vertex& v1, - const Vertex& v2, - const Vertex& v3 - ) const { - double cur_m; - double cur_Vg[DIM]; - Geom::triangle_centroid( - v1.point(), v2.point(), v3.point(), - v1.weight(), v2.weight(), v3.weight(), - cur_Vg, cur_m, DIM - ); - locks_.acquire_spinlock(v); - m_[v] += cur_m; - double* cur_mg_out = mg_ + v * DIM; - for(coord_index_t coord = 0; coord < DIM; coord++) { - cur_mg_out[coord] += cur_Vg[coord]; - } - locks_.release_spinlock(v); - } - - private: - double* mg_; - double* m_; - LOCKS& locks_; - }; - - void compute_centroids_on_surface(double* mg, double* m) override { - create_threads(); - if(nb_parts() == 0) { - if(master_ != nullptr) { - if(has_weights_) { - RVD_.for_each_triangle( - ComputeCentroidsWeighted( - mg, m, master_->spinlocks_ - ) - ); - } else { - RVD_.for_each_triangle( - ComputeCentroids( - mg, m, master_->spinlocks_ - ) - ); - } - } else { - NoLocks nolocks; - if(has_weights_) { - RVD_.for_each_triangle( - ComputeCentroidsWeighted( - mg, m, nolocks - ) - ); - } else { - RVD_.for_each_triangle( - ComputeCentroids(mg, m, nolocks) - ); - } - } - } else { - thread_mode_ = MT_LLOYD; - arg_vectors_ = mg; - arg_scalars_ = m; - spinlocks_.resize(delaunay_->nb_vertices()); - parallel_for( - 0, nb_parts(), - std::bind(&thisclass::run_thread, this, std::placeholders::_1) - ); - } - } - - /********************************************************************/ - - /** - * \brief Implementation class for surfacic Lloyd relaxation. - * \details To be used as a template argument - * to RVD::for_each_volumetric_integration_simplex(). - * This version ignores the weights. - * - * Computes for each RVD cell: - * - mg[v] (v's Voronoi cell's total area times centroid) - * - m[v] (v's total area) - * \tparam LOCKS locking policy - * (can be one of Process::SpinLockArray, NoLocks) - */ - template - class ComputeCentroidsVolumetric { - public: - /** - * \brief Constructs a ComputeCentroidsVolumetric. - * \param[out] mg where to store the centroids - * \param[out] m where to store the masses - * \param[in] delaunay the Delaunay triangulation - * \param[in] locks the array of locks - * (or NoLocks in single thread mode) - */ - ComputeCentroidsVolumetric( - double* mg, - double* m, - const Delaunay* delaunay, - LOCKS& locks - ) : - mg_(mg), - m_(m), - delaunay_(delaunay), - locks_(locks) { - } - - /** - * \brief The callback called for each integration simplex. - * \param[in] v index of current center vertex - * \param[in] v_adj (unused here) is the index of the Voronoi cell - * adjacent to t accros facet (\p v1, \p v2, \p v3) or - * -1 if it does not exists - * \param[in] t (unused here) is the index of the current - * tetrahedron - * \param[in] t_adj (unused here) is the index of the - * tetrahedron adjacent to t accros facet (\p v1, \p v2, \p v3) - * or -1 if it does not exists - * \param[in] p0 first vertex of current integration simplex - * \param[in] p1 second vertex of current integration simplex - * \param[in] p2 third vertex of current integration simplex - * \param[in] p3 fourth vertex of current integration simplex - */ - void operator() ( - index_t v, signed_index_t v_adj, - index_t t, signed_index_t t_adj, - const double* p0, - const double* p1, - const double* p2, - const double* p3 - ) const { - geo_argused(v_adj); - geo_argused(t); - geo_argused(t_adj); - double cur_m = Geom::tetra_volume( - p0, p1, p2, p3 - ); - double s = cur_m / 4.0; - locks_.acquire_spinlock(v); - m_[v] += cur_m; - double* cur_mg_out = mg_ + v * DIM; - for(coord_index_t coord = 0; coord < DIM; coord++) { - cur_mg_out[coord] += s * ( - p0[coord] + p1[coord] + p2[coord] + p3[coord] - ); - } - locks_.release_spinlock(v); - } - - private: - double* mg_; - double* m_; - const Delaunay* delaunay_; - LOCKS& locks_; - }; - - void compute_centroids_in_volume(double* mg, double* m) override { - create_threads(); - if(nb_parts() == 0) { - if(master_ != nullptr) { - RVD_.for_each_tetrahedron( - ComputeCentroidsVolumetric( - mg, m, RVD_.delaunay(), master_->spinlocks_ - ) - ); - } else { - NoLocks nolocks; - RVD_.for_each_tetrahedron( - ComputeCentroidsVolumetric( - mg, m, RVD_.delaunay(), nolocks - ) - ); - } - } else { - thread_mode_ = MT_LLOYD; - arg_vectors_ = mg; - arg_scalars_ = m; - spinlocks_.resize(delaunay_->nb_vertices()); - parallel_for( - 0, nb_parts(), - std::bind(&thisclass::run_thread, this, std::placeholders::_1) - ); - } - } - - /********************************************************************/ - - /** - * \brief Implementation class for Newton-based restricted CVT. - * \details To be used as a template argument - * to RVD::for_each_triangle(). - * This version ignores the weights. - * - * Computes for each RVD cell: - * - g (gradient) - * - f (CVT energy) - * \tparam LOCKS locking policy - * (can be one of Process::SpinLockArray, NoLocks) - */ - template - class ComputeCVTFuncGrad { - public: - /** - * \brief Constructs a ComputeCVTFuncGrad. - * \param[in] RVD the restricted Voronoi diagram - * \param[out] f the computed function value - * \param[out] g the computed gradient of f, - * allocated by caller, and managed - * by caller - * \param[in] locks the array of locks - * (or NoLocks in single thread mode) - */ - ComputeCVTFuncGrad( - const GenRestrictedVoronoiDiagram& RVD, - double& f, - double* g, - LOCKS& locks - ) : - f_(f), - g_(g), - locks_(locks), - RVD_(RVD) { - } - - /** - * \brief The callback called for each integration simplex. - * \param[in] v index of current center vertex - * \param[in] p1 first vertex of current integration simplex - * \param[in] p2 second vertex of current integration simplex - * \param[in] p3 third vertex of current integration simplex - */ - void operator() ( - index_t v, - const double* p1, - const double* p2, - const double* p3 - ) const { - - const double* p0 = RVD_.delaunay()->vertex_ptr(v); - - double t_area = Geom::triangle_area(p1, p2, p3, DIM); - - double cur_f = 0.0; - for(index_t c = 0; c < DIM; c++) { - double u0 = p0[c] - p1[c]; - double u1 = p0[c] - p2[c]; - double u2 = p0[c] - p3[c]; - cur_f += u0 * u0; - cur_f += u1 * (u0 + u1); - cur_f += u2 * (u0 + u1 + u2); - } - - f_ += t_area * cur_f / 6.0; - - locks_.acquire_spinlock(v); - for(index_t c = 0; c < DIM; c++) { - double Gc = (1.0 / 3.0) * (p1[c] + p2[c] + p3[c]); - g_[DIM * v + c] += (2.0 * t_area) * (p0[c] - Gc); - } - locks_.release_spinlock(v); - } - - double& f_; - double* g_; - LOCKS& locks_; - const GenRestrictedVoronoiDiagram& RVD_; - }; - - /** - * \brief Implementation class for Newton-based restricted CVT. - * \details To be used as a template argument - * to RVD::for_each_triangle(). - * This version takes the weights into account. - * - * Computes for each RVD cell: - * - g (gradient) - * - f (CVT energy) - * \tparam LOCKS locking policy - * (can be one of Process::SpinLockArray, NoLocks) - */ - template - class ComputeCVTFuncGradWeighted { - public: - /** - * \brief Constructs a ComputeCVTFuncGradWeighted. - * \param[in] RVD the restricted Voronoi diagram - * \param[out] f the computed function value - * \param[out] g the computed gradient of f, - * allocated by caller, and managed by caller - * \param[in] locks the array of locks - * (or NoLocks in single thread mode) - */ - ComputeCVTFuncGradWeighted( - const GenRestrictedVoronoiDiagram& RVD, - double& f, - double* g, - LOCKS& locks - ) : - f_(f), - g_(g), - locks_(locks), - RVD_(RVD) { - } - - /** - * \brief The callback called for each integration simplex. - * \param[in] v index of current center vertex - * \param[in] v1 first vertex of current integration simplex - * \param[in] v2 second vertex of current integration simplex - * \param[in] v3 third vertex of current integration simplex - */ - void operator() ( - index_t v, - const Vertex& v1, - const Vertex& v2, - const Vertex& v3 - ) const { - - const double* p0 = RVD_.delaunay()->vertex_ptr(v); - - const double* p1 = v1.point(); - const double* p2 = v2.point(); - const double* p3 = v3.point(); - - double t_area = Geom::triangle_area(p1, p2, p3, DIM); - - double Sp = v1.weight() + v2.weight() + v3.weight(); - double rho[3], alpha[3]; - rho[0] = v1.weight(); - rho[1] = v2.weight(); - rho[2] = v3.weight(); - alpha[0] = Sp + rho[0]; - alpha[1] = Sp + rho[1]; - alpha[2] = Sp + rho[2]; - - double dotprod_00 = 0.0; - double dotprod_10 = 0.0; - double dotprod_11 = 0.0; - double dotprod_20 = 0.0; - double dotprod_21 = 0.0; - double dotprod_22 = 0.0; - for(unsigned int c = 0; c < DIM; c++) { - double sp0 = p0[c] - p1[c]; - double sp1 = p0[c] - p2[c]; - double sp2 = p0[c] - p3[c]; - dotprod_00 += sp0 * sp0; - dotprod_10 += sp1 * sp0; - dotprod_11 += sp1 * sp1; - dotprod_20 += sp2 * sp0; - dotprod_21 += sp2 * sp1; - dotprod_22 += sp2 * sp2; - } - - double cur_f = 0.0; - cur_f += (alpha[0] + rho[0]) * dotprod_00; // 0 0 - cur_f += (alpha[1] + rho[0]) * dotprod_10; // 1 0 - cur_f += (alpha[1] + rho[1]) * dotprod_11; // 1 1 - cur_f += (alpha[2] + rho[0]) * dotprod_20; // 2 0 - cur_f += (alpha[2] + rho[1]) * dotprod_21; // 2 1 - cur_f += (alpha[2] + rho[2]) * dotprod_22; // 2 2 - - f_ += t_area * cur_f / 30.0; - double* g_out = g_ + v * DIM; - locks_.acquire_spinlock(v); - for(index_t c = 0; c < DIM; c++) { - g_out[c] += (t_area / 6.0) * ( - 4.0 * Sp * p0[c] - ( - alpha[0] * p1[c] + - alpha[1] * p2[c] + - alpha[2] * p3[c] - ) - ); - } - locks_.release_spinlock(v); - } - - double& f_; - double* g_; - LOCKS& locks_; - const GenRestrictedVoronoiDiagram& RVD_; - }; - - void compute_CVT_func_grad_on_surface(double& f, double* g) override { - create_threads(); - if(nb_parts() == 0) { - if(master_ != nullptr) { - if(has_weights_) { - RVD_.for_each_triangle( - ComputeCVTFuncGradWeighted( - RVD_, f, g, master_->spinlocks_ - ) - ); - } else { - RVD_.for_each_triangle( - ComputeCVTFuncGrad( - RVD_, f, g, master_->spinlocks_ - ) - ); - } - } else { - NoLocks nolocks; - if(has_weights_) { - RVD_.for_each_triangle( - ComputeCVTFuncGradWeighted( - RVD_, f, g, nolocks - ) - ); - } else { - RVD_.for_each_triangle( - ComputeCVTFuncGrad( - RVD_, f, g, nolocks - ) - ); - } - } - } else { - thread_mode_ = MT_NEWTON; - arg_vectors_ = g; - spinlocks_.resize(delaunay_->nb_vertices()); - for(index_t t = 0; t < nb_parts(); t++) { - part(t).funcval_ = 0.0; - } - parallel_for( - 0, nb_parts(), - std::bind(&thisclass::run_thread, this, std::placeholders::_1) - ); - for(index_t t = 0; t < nb_parts(); t++) { - f += part(t).funcval_; - } - } - } - - /********************************************************************/ - - /** - * \brief Implementation class for Newton-based restricted CVT - * in volume. - * \details To be used as a template argument - * to RVD::for_each_volumetric_integration_simplex(). - * This version ignores the weights. - * - * Computes for each RVD cell: - * - g (gradient) - * - f (CVT energy) - * \tparam LOCKS locking policy - * (can be one of Process::SpinLockArray, NoLocks) - */ - template - class ComputeCVTFuncGradVolumetric { - public: - /** - * \brief Constructs a ComputeCentroidsFuncGradVolumetric. - * \param[in] RVD the restricted Voronoi diagram - * \param[out] f the computed function value - * \param[out] g the computed gradient of f, - * allocated by caller, and managed - * by caller - * \param[in] locks the array of locks - * (or NoLocks in single thread mode) - */ - ComputeCVTFuncGradVolumetric( - const GenRestrictedVoronoiDiagram& RVD, - double& f, - double* g, - LOCKS& locks - ) : - f_(f), - g_(g), - locks_(locks), - RVD_(RVD) { - } - - /** - * \brief The callback called for each integration simplex. - * \param[in] v index of current center vertex - * \param[in] v_adj (unused here) is the index of the Voronoi cell - * adjacent to t accros facet (\p v1, \p v2, \p v3) or - * -1 if it does not exists - * \param[in] t (unused here) is the index of the current - * tetrahedron - * \param[in] t_adj (unused here) is the index of the - * tetrahedron adjacent to t accros facet (\p v1, \p v2, \p v3) - * or -1 if it does not exists - * \param[in] p1 first vertex of current integration simplex - * \param[in] p2 second vertex of current integration simplex - * \param[in] p3 third vertex of current integration simplex - */ - void operator() ( - index_t v, - signed_index_t v_adj, - index_t t, - signed_index_t t_adj, - const double* p1, - const double* p2, - const double* p3 - ) const { - geo_argused(v_adj); - geo_argused(t); - geo_argused(t_adj); - const double* p0 = RVD_.delaunay()->vertex_ptr(v); - - double mi = Geom::tetra_volume(p0, p1, p2, p3); - - // fi = (mi/10)*(U.U + V.V + W.W + U.V + V.W + W.U) - // where: U = p1-p0 ; V = p2-p0 and W=p3-p0 - double fi = 0.0; - for(coord_index_t c = 0; c < DIM; ++c) { - double Uc = p1[c] - p0[c]; - double Vc = p2[c] - p0[c]; - double Wc = p3[c] - p0[c]; - fi += geo_sqr(Uc) + geo_sqr(Vc) + geo_sqr(Wc); - fi += (Uc * Vc + Vc * Wc + Wc * Uc); - } - fi *= (mi / 10.0); - f_ += fi; - - // gi = 2*mi(p0 - 1/4(p0 + p1 + p2 + p3)) - double* g_out = g_ + v * DIM; - locks_.acquire_spinlock(v); - for(coord_index_t c = 0; c < DIM; ++c) { - g_out[c] += 2.0 * mi * ( - 0.75 * p0[c] - - 0.25 * p1[c] - 0.25 * p2[c] - 0.25 * p3[c] - ); - } - locks_.release_spinlock(v); - } - - double& f_; - double* g_; - LOCKS& locks_; - const GenRestrictedVoronoiDiagram& RVD_; - }; - - void compute_CVT_func_grad_in_volume(double& f, double* g) override { - create_threads(); - if(nb_parts() == 0) { - if(master_ != nullptr) { - RVD_.for_each_volumetric_integration_simplex( - ComputeCVTFuncGradVolumetric( - RVD_, f, g, master_->spinlocks_ - ) - ); - } else { - NoLocks nolocks; - RVD_.for_each_volumetric_integration_simplex( - ComputeCVTFuncGradVolumetric( - RVD_, f, g, nolocks - ) - ); - } - } else { - thread_mode_ = MT_NEWTON; - arg_vectors_ = g; - spinlocks_.resize(delaunay_->nb_vertices()); - for(index_t t = 0; t < nb_parts(); t++) { - part(t).funcval_ = 0.0; - } - parallel_for( - 0, nb_parts(), - std::bind(&thisclass::run_thread, this, std::placeholders::_1) - ); - for(index_t t = 0; t < nb_parts(); t++) { - f += part(t).funcval_; - } - } - } - - /********************************************************************/ - - /** - * \brief Implementation class for computing function integrals - * and gradients over integration simplices. - * \details To be used as a template argument - * to RVD::for_each_triangle() and - * RVD::for_each_volumetric_integration_simplex() - */ - class ComputeCVTFuncGradIntegrationSimplex { - public: - /** - * \brief Constructs a ComputeCVTFuncGradIntegrationSimplex. - * \param[in] RVD the Restricted Voronoi Diagram - * \param[in] F the IntegrationSimplex - */ - ComputeCVTFuncGradIntegrationSimplex( - const GenRestrictedVoronoiDiagram& RVD, - IntegrationSimplex* F - ) : - f_(0.0), - RVD_(RVD), - simplex_func_(F) { - simplex_func_->reset_thread_local_storage(); - } - - /** - * \brief The callback called for each surfacic integration simplex. - * \param[in] i index of current center vertex - * \param[in] v1 first vertex of current integration simplex - * \param[in] v2 second vertex of current integration simplex - * \param[in] v3 third vertex of current integration simplex - */ - void operator() ( - index_t i, - const Vertex& v1, - const Vertex& v2, - const Vertex& v3 - ) { - f_ += simplex_func_->eval( - i,v1,v2,v3,RVD_.current_facet() - ); - } - - /** - * \brief The callback called for each volumetric - * integration simplex. - * \param[in] v index of current center vertex - * \param[in] v_adj index of the Voronoi cell adjacent to t accros - * facet (\p v1, \p v2, \p v3) or -1 if it does not exists - * \param[in] t index of the current tetrahedron - * \param[in] t_adj index of the tetrahedron adjacent to t accros - * facet (\p v1, \p v2, \p v3) or -1 if it does not exists - * \param[in] v1 first vertex of current integration simplex - * \param[in] v2 second vertex of current integration simplex - * \param[in] v3 third vertex of current integration simplex - */ - void operator() ( - index_t v, - signed_index_t v_adj, - index_t t, - signed_index_t t_adj, - const Vertex& v1, - const Vertex& v2, - const Vertex& v3 - ) { - geo_argused(v_adj); - geo_argused(t_adj); - f_ += simplex_func_->eval( - v,v1,v2,v3,t,index_t(t_adj),index_t(v_adj) - ); - } - - /** - * \brief Gets the function value. - * \return The function value accumulated so far. - */ - double f() const { - return f_; - } - - private: - double f_; - const GenRestrictedVoronoiDiagram& RVD_; - IntegrationSimplex* simplex_func_; - }; - - void compute_integration_simplex_func_grad( - double& f, double* g, IntegrationSimplex* F - ) override { - create_threads(); - if(nb_parts() == 0) { - if(master_ == nullptr) { - F->set_points_and_gradient( - delaunay()->dimension(), - delaunay()->nb_vertices(), - delaunay()->vertex_ptr(0), - g - ); - } - ComputeCVTFuncGradIntegrationSimplex C(RVD_,F); - bool sym = RVD_.symbolic(); - RVD_.set_symbolic(true); - if(F->volumetric()) { - RVD_.for_each_volumetric_integration_simplex( - C, - F->background_mesh_has_varying_attribute(), - false /* Coherent triangles */ - ); - } else { - RVD_.for_each_triangle(C); - } - RVD_.set_symbolic(sym); - funcval_ = C.f(); - f = C.f(); - } else { - spinlocks_.resize(delaunay_->nb_vertices()); - F->set_points_and_gradient( - delaunay()->dimension(), - delaunay()->nb_vertices(), - delaunay()->vertex_ptr(0), - g, - &spinlocks_ - ); - thread_mode_ = MT_INT_SMPLX; - arg_vectors_ = g; - simplex_func_ = F; - funcval_ = 0.0; - for(index_t t = 0; t < nb_parts(); t++) { - part(t).arg_vectors_ = g; - part(t).simplex_func_ = F; - part(t).funcval_ = 0.0; - } - - parallel_for( - 0, nb_parts(), - std::bind(&thisclass::run_thread, this, std::placeholders::_1) - ); - - f = 0.0; - for(index_t t = 0; t < nb_parts(); t++) { - f += part(t).funcval_; - } - } - } - - /********************************************************************/ - - /** - * \brief Adapter class used internally to implement for_each_polygon() - * \details Gets the current triangle from the RVD and passes it back - * to the callback. It is needed because GenericRVD::for_each_polygon() - * does not pass the current triangle. - */ - // TODO: pass it through all the callbacks, because it is ridiculous: - // we pass it through the first levels, then throw it, then retrieve it - // (see GenRVD) - class PolygonCallbackAction { - public: - /** - * \brief PolygonCallbackAction constructor - * \param[in] RVD a pointer to the restricted Voronoi diagram - * \param[in] callback a pointer to the PolygonCallback - */ - PolygonCallbackAction( - GenRestrictedVoronoiDiagram& RVD, - GEO::RVDPolygonCallback& callback - ) : - RVD_(RVD), - callback_(callback) { - } - - /** - * \brief Callback called for each polygon. - * \details Routes the callback to the wrapped user action class. - * \param[in] v index of current Delaunay seed - * \param[in] P intersection between current mesh facet - * and the Voronoi cell of \p v - */ - void operator() ( - index_t v, - const GEOGen::Polygon& P - ) const { - callback_(v, RVD_.current_facet(), P); - } - - protected: - GenRestrictedVoronoiDiagram& RVD_; - GEO::RVDPolygonCallback& callback_; - }; - - - virtual void compute_with_polygon_callback( - GEO::RVDPolygonCallback& polygon_callback - ) { - create_threads(); - if(nb_parts() == 0) { - PolygonCallbackAction action(RVD_,polygon_callback); - RVD_.for_each_polygon(action); - } else { - for(index_t t = 0; t < nb_parts(); t++) { - part(t).RVD_.set_symbolic(RVD_.symbolic()); - part(t).RVD_.set_connected_components_priority( - RVD_.connected_components_priority() - ); - } - spinlocks_.resize(delaunay_->nb_vertices()); - thread_mode_ = MT_POLYG; - polygon_callback_ = &polygon_callback; - polygon_callback_->set_spinlocks(&spinlocks_); - // Note: callback begin()/end() is called in for_each_polygon() - parallel_for( - 0, nb_parts(), - std::bind(&thisclass::run_thread, this, std::placeholders::_1) - ); - polygon_callback_->set_spinlocks(nullptr); - } - } - - virtual void compute_with_polyhedron_callback( - GEO::RVDPolyhedronCallback& polyhedron_callback - ) { - create_threads(); - if(nb_parts() == 0) { - RVD_.for_each_polyhedron(polyhedron_callback); - } else { - for(index_t t = 0; t < nb_parts(); t++) { - part(t).RVD_.set_symbolic(RVD_.symbolic()); - part(t).RVD_.set_connected_components_priority( - RVD_.connected_components_priority() - ); - } - spinlocks_.resize(delaunay_->nb_vertices()); - thread_mode_ = MT_POLYH; - polyhedron_callback_ = &polyhedron_callback; - polyhedron_callback_->set_spinlocks(&spinlocks_); - // Note: callback begin()/end() is - // called in for_each_polyhedron() - parallel_for( - 0, nb_parts(), - std::bind(&thisclass::run_thread, this, std::placeholders::_1) - ); - polyhedron_callback_->set_spinlocks(nullptr); - } - } - - /********************************************************************/ - - /** - * \brief Implementation class for explicitly constructing - * a surfacic mesh that corresponds to the surfacic - * restricted Voronoi diagram. - * \details To be used as a template argument - * to RVD::for_each_polygon(). The current Vornoi cell is - * reported in facet region. - * \tparam BUILDER a class that implements iterative mesh building, - * e.g., MeshBuilder. - */ - template - class BuildRVD { - public: - /** - * \brief Constructs a new BuildRVD. - * \param[in] RVD_in the restricted Voronoi diagram - * \param[in] builder the lesh builder - */ - BuildRVD( - const GenRestrictedVoronoiDiagram& RVD_in, - BUILDER& builder - ) : - RVD(RVD_in), - builder_(builder), - current_facet_(-1) { - builder_.begin_surface(); - } - - /** - * \brief The destructor - * \details Terminates the current facet - * and the current surface. - */ - ~BuildRVD() { - if(current_facet_ != -1) { - builder_.end_reference_facet(); - } - builder_.end_surface(); - } - - /** - * \brief The callback called for each restricted Voronoi cell. - * \param[in] v index of current center vertex - * \param[in] P current restricted Voronoi cell - */ - void operator() ( - index_t v, - const typename GenRestrictedVoronoiDiagram::Polygon& P - ) { - index_t f = RVD.current_facet(); - if(signed_index_t(f) != current_facet_) { - if(current_facet_ != -1) { - builder_.end_reference_facet(); - } - current_facet_ = signed_index_t(f); - builder_.begin_reference_facet(f); - } - builder_.begin_facet(v); - for(index_t i = 0; i < P.nb_vertices(); i++) { - const Vertex& ve = P.vertex(i); - builder_.add_vertex_to_facet(ve.point(), ve.sym()); - } - builder_.end_facet(); - } - - private: - const GenRestrictedVoronoiDiagram& RVD; - BUILDER& builder_; - signed_index_t current_facet_; - }; - - /** - * \brief Implementation class for explicitly constructing - * a volumetric mesh that corresponds to the volumetric - * restricted Voronoi diagram. - * \details To be used as a template argument - * to RVD::for_each_volumetric_integration_simplex(). - * The current Voronoi cell is reported in tetrahedron region. - * \note For the moment, vertices are duplicated (will be fixed - * in a future version). - */ - class BuildVolumetricRVD { - public: - /** - * Constructs a new BuildVolumetricRVD. - * \param[in] RVD the volumetric restricted Voronoi diagram - * \param[in] dim dimension of the points (can be smaller - * than actual dimension of the RVD). - * \param[out] vertices coordinates of the generated vertices - * \param[out] triangle_vertex_indices generated triangles, as - * vertex indices triplets - * \param[out] tet_vertex_indices generated tetrahedra, as - * vertex indices 4-uples - * \param[out] triangle_regions each generated triangle has - * a region index, that corresponds to the index of the - * Voronoi cell the triangle belongs to - * \param[out] tet_regions each generated tetrahedron has - * a region index, that corresponds to the index of the - * Voronoi cell the tetrahedron belongs to - * \param[in] cell_borders_only if true, only the surfacic - * borders of the volumetric cells are saved in the mesh, else - * volumetric cells are tetrahedralized. - * \pre dim <= delaunay->dimension() - */ - BuildVolumetricRVD( - GenRestrictedVoronoiDiagram& RVD, - coord_index_t dim, - vector& vertices, - vector& triangle_vertex_indices, - vector& tet_vertex_indices, - vector& triangle_regions, - vector& tet_regions, - bool cell_borders_only - ) : - delaunay_(RVD.delaunay()), - mesh_(RVD.mesh()), - dim_(dim), - vertices_(vertices), - triangle_vertex_indices_(triangle_vertex_indices), - tet_vertex_indices_(tet_vertex_indices), - triangle_regions_(triangle_regions), - tet_regions_(tet_regions), - cell_borders_only_(cell_borders_only) - { - vertices_.clear(); - triangle_vertex_indices_.clear(); - tet_vertex_indices_.clear(); - triangle_regions_.clear(); - tet_regions_.clear(); - - // The first vertices are copied from Delaunay, - // the other ones will be created during the traversal - nb_vertices_ = delaunay_->nb_vertices(); - vertices_.resize(nb_vertices_ * dim); - for(index_t v = 0; v < delaunay_->nb_vertices(); ++v) { - for(coord_index_t c = 0; c < dim; ++c) { - vertices_[v * dim + c] = delaunay_->vertex_ptr(v)[c]; - } - } - vertex_map_.set_first_vertex_index(nb_vertices_); - } - - /** - * \brief The callback called for each integration simplex. - * \param[in] v index of current center vertex - * \param[in] v_adj (unused here) is the index of the Voronoi cell - * adjacent to t accros facet (\p v1, \p v2, \p v3) or - * -1 if it does not exists - * \param[in] t (unused here) is the index of the current - * tetrahedron - * \param[in] t_adj (unused here) is the index of the - * tetrahedron adjacent to t accros facet (\p v1, \p v2, \p v3) - * or -1 if it does not exists - * \param[in] v1 first vertex of current integration simplex - * \param[in] v2 second vertex of current integration simplex - * \param[in] v3 third vertex of current integration simplex - */ - void operator() ( - index_t v, signed_index_t v_adj, - index_t t, signed_index_t t_adj, - const Vertex& v1, const Vertex& v2, const Vertex& v3 - ) { - geo_argused(v_adj); - geo_argused(t); - - if(cell_borders_only_) { - if(signed_index_t(v) > v_adj) { - index_t iv2 = find_or_create_vertex(v, v1); - index_t iv3 = find_or_create_vertex(v, v2); - index_t iv4 = find_or_create_vertex(v, v3); - triangle_vertex_indices_.push_back(iv4); - triangle_vertex_indices_.push_back(iv3); - triangle_vertex_indices_.push_back(iv2); - triangle_regions_.push_back(signed_index_t(v)); - } - } else { - index_t iv1 = v; - index_t iv2 = find_or_create_vertex(v, v1); - index_t iv3 = find_or_create_vertex(v, v2); - index_t iv4 = find_or_create_vertex(v, v3); - - // Triangle v1,v2,v3 is on border if there is - // no adjacent seed and no adjacent tet. - if(v_adj == -1 && t_adj == -1) { - triangle_vertex_indices_.push_back(iv4); - triangle_vertex_indices_.push_back(iv3); - triangle_vertex_indices_.push_back(iv2); - triangle_regions_.push_back(signed_index_t(v)); - } - - tet_vertex_indices_.push_back(iv1); - tet_vertex_indices_.push_back(iv2); - tet_vertex_indices_.push_back(iv3); - tet_vertex_indices_.push_back(iv4); - tet_regions_.push_back(signed_index_t(v)); - } - } - - /** - * \brief The callback called for each tetrahedron - * \param[in] v index of current center vertex - * \param[in] v_adj (unused here) is the index of the Voronoi cell - * adjacent to t accros facet (\p v1, \p v2, \p v3) or - * -1 if it does not exists - * \param[in] t (unused here) is the index of the current - * tetrahedron - * \param[in] t_adj (unused here) is the index of the - * tetrahedron adjacent to t accros facet (\p v1, \p v2, \p v3) - * or -1 if it does not exists - * \param[in] v1 first vertex of current tetrahedron - * \param[in] v2 second vertex of current tetrahedron - * \param[in] v3 third vertex of current tetrahedron - * \param[in] v4 fourth vertex of current tetrahedron - */ - void operator() ( - index_t v, signed_index_t v_adj, - index_t t, signed_index_t t_adj, - const Vertex& v1, const Vertex& v2, - const Vertex& v3, const Vertex& v4 - ) { - geo_argused(v_adj); - geo_argused(t); - geo_argused(t_adj); - index_t iv1 = vertices_.size() / dim_; - for(index_t c = 0; c < dim_; ++c) { - vertices_.push_back(v1.point()[c]); - } - index_t iv2 = vertices_.size() / dim_; - for(index_t c = 0; c < dim_; ++c) { - vertices_.push_back(v2.point()[c]); - } - index_t iv3 = vertices_.size() / dim_; - for(index_t c = 0; c < dim_; ++c) { - vertices_.push_back(v3.point()[c]); - } - index_t iv4 = vertices_.size() / dim_; - for(index_t c = 0; c < dim_; ++c) { - vertices_.push_back(v4.point()[c]); - } - tet_vertex_indices_.push_back(iv1); - tet_vertex_indices_.push_back(iv2); - tet_vertex_indices_.push_back(iv3); - tet_vertex_indices_.push_back(iv4); - tet_regions_.push_back(signed_index_t(v)); - } - - protected: - /** - * \brief Retrieves the index of a vertex given its symbolic - * representation. - * \param[in] center_vertex_id index of current Voronoi seed - * \param[in] v symbolic and geometric representation of the vertex - * \return the index of the vertex - */ - index_t find_or_create_vertex( - index_t center_vertex_id, const Vertex& v - ) { - index_t result = vertex_map_.find_or_create_vertex( - center_vertex_id, v.sym() - ); - if(result >= nb_vertices_) { - geo_assert(result == nb_vertices_); - nb_vertices_ = result + 1; - for(coord_index_t c = 0; c < dim_; ++c) { - vertices_.push_back(v.point()[c]); - } - } - return result; - } - - private: - const Delaunay* delaunay_; - const Mesh* mesh_; - coord_index_t dim_; - vector& vertices_; - vector& triangle_vertex_indices_; - vector& tet_vertex_indices_; - vector& triangle_regions_; - vector& tet_regions_; - RVDVertexMap vertex_map_; - index_t nb_vertices_; - bool cell_borders_only_; - }; - - void compute_RVD( - Mesh& M, coord_index_t dim, bool cell_borders_only, - bool integration_simplices - ) override { - bool sym = RVD_.symbolic(); - RVD_.set_symbolic(true); - if(volumetric_) { - if(dim == 0) { - dim = dimension(); - } - vector vertices; - vector triangle_vertices; - vector tet_vertices; - vector triangle_regions; - vector tet_regions; - if(cell_borders_only) { - RVD_.for_each_volumetric_integration_simplex( - BuildVolumetricRVD( - RVD_, dim, - vertices, - triangle_vertices, - tet_vertices, - triangle_regions, - tet_regions, - cell_borders_only - ), - false, // Do not visit inner tetrahedra. - true // Ensure that polygonal facets are triangulated - // coherently. - ); - } else { - if(integration_simplices) { - RVD_.for_each_volumetric_integration_simplex( - BuildVolumetricRVD( - RVD_, dim, - vertices, - triangle_vertices, - tet_vertices, - triangle_regions, - tet_regions, - cell_borders_only - ), - false, // Do not visit inner tetrahedra. - true // Ensure that polygonal facets are - // triangulated coherently. - ); - } else { - RVD_.for_each_tetrahedron( - BuildVolumetricRVD( - RVD_, dim, - vertices, - triangle_vertices, - tet_vertices, - triangle_regions, - tet_regions, - cell_borders_only - ) - ); - } - } - - M.clear(true); // keep attributes - - M.vertices.assign_points(vertices,dim,true); - M.facets.assign_triangle_mesh(triangle_vertices, true); - M.cells.assign_tet_mesh(tet_vertices, true); - - // TODO: use Attribute::assign(vector, steal_args) - // when it is there... - - if(M.facets.nb() != 0) { - Attribute facet_region_attr( - M.facets.attributes(), "region" - ); - for(index_t f=0; f cell_region_attr( - M.cells.attributes(), "region" - ); - for(index_t c=0; c(RVD_, builder) - ); - } - RVD_.set_symbolic(sym); - M.show_stats("RVD"); - } - - /********************************************************************/ - - void compute_RVC( - index_t i, - Mesh& M, - Mesh& result, - bool copy_symbolic_info - ) override { - Mesh* tmp_mesh = mesh_; - mesh_ = &M; - RVD_.set_mesh(&M); - typename GenRestrictedVoronoiDiagram::Polyhedron Cell(dimension()); - Cell.initialize_from_surface_mesh(&M, RVD_.symbolic()); - RVD_.intersect_cell_cell(i, Cell); - Cell.convert_to_mesh(&result, copy_symbolic_info); - mesh_ = tmp_mesh; - RVD_.set_mesh(tmp_mesh); - } - - /********************************************************************/ - - void for_each_polygon( - GEO::RVDPolygonCallback& callback, - bool symbolic, - bool connected_comp_priority, - bool parallel - ) override { - bool sym_backup = RVD_.symbolic(); - RVD_.set_symbolic(symbolic); - RVD_.set_connected_components_priority(connected_comp_priority); - callback.begin(); - if(parallel) { - compute_with_polygon_callback(callback); - } else { - PolygonCallbackAction action(RVD_,callback); - RVD_.for_each_polygon(action); - } - callback.end(); - RVD_.set_symbolic(sym_backup); - RVD_.set_connected_components_priority(false); - } - - /********************************************************************/ - - void for_each_polyhedron( - GEO::RVDPolyhedronCallback& callback, - bool symbolic, - bool connected_comp_priority, - bool parallel - ) override { - bool sym_backup = RVD_.symbolic(); - RVD_.set_symbolic(symbolic); - RVD_.set_connected_components_priority(connected_comp_priority); - callback.set_dimension(RVD_.mesh()->vertices.dimension()); - callback.begin(); - if(parallel) { - compute_with_polyhedron_callback(callback); - } else { - RVD_.for_each_polyhedron(callback); - } - callback.end(); - RVD_.set_symbolic(sym_backup); - RVD_.set_connected_components_priority(false); - } - - /********************************************************************/ - - /** - * \brief Does the actual computation for a specific part - * in multithread mode. - * \param[in] t the index of the part. - * \pre \p t < nb_parts() - */ - void run_thread(index_t t) { - geo_assert(t < nb_parts()); - thisclass& T = part(t); - switch(thread_mode_) { - case MT_LLOYD: - { - T.compute_centroids(arg_vectors_, arg_scalars_); - } break; - case MT_NEWTON: - { - T.compute_CVT_func_grad(T.funcval_, arg_vectors_); - } break; - case MT_INT_SMPLX: - { - T.compute_integration_simplex_func_grad( - T.funcval_, arg_vectors_, simplex_func_ - ); - } break; - case MT_POLYG: - { - T.compute_with_polygon_callback( - *polygon_callback_ - ); - } break; - case MT_POLYH: - { - T.compute_with_polyhedron_callback( - *polyhedron_callback_ - ); - } break; - case MT_NONE: - geo_assert_not_reached; - } - } - - bool compute_initial_sampling_on_surface( - double* p, index_t nb_points - ) override { - geo_assert(mesh_->facets.are_simplices()); - - // We do that here, since this triggers partitioning, - // that improves data locality. Then data locality is - // inherited by the generated points. - create_threads(); - - if(facets_begin_ == -1 && facets_end_ == -1) { - Logger::out("RVD") - << "Computing initial sampling on surface, using dimension=" - << index_t(dimension_) << std::endl; - } - - return mesh_generate_random_samples_on_surface( - *mesh_, p, nb_points, vertex_weight_, facets_begin_, facets_end_ - ); - } - - bool compute_initial_sampling_in_volume( - double* p, index_t nb_points - ) override { - geo_assert(mesh_->cells.nb() != 0); - - // We do that here, since this triggers partitioning, - // that improves data locality. Then data locality is - // inherited by the generated points. - create_threads(); - - if(tets_begin_ == -1 && tets_end_ == -1) { - Logger::out("RVD") - << "Computing initial sampling in volume, using dimension=" - << index_t(dimension_) << std::endl; - } - - return mesh_generate_random_samples_in_volume( - *mesh_, p, nb_points, vertex_weight_, tets_begin_, tets_end_ - ); - } - - /** - * \brief Creates the data structures for fast projection. - * \details It decomposes the surface into triangles, and - * stores the vertices in a KdTree. - */ - void prepare_projection() { - if(!mesh_vertices_.is_null()) { - return; - } - - // Step 1: get triangles - for(index_t f = 0; f < mesh_->facets.nb(); f++) { - index_t i = mesh_->facets.corners_begin(f); - for( - index_t j = i + 1; - j + 1 < mesh_->facets.corners_end(f); j++ - ) { - triangles_.push_back(mesh_->facet_corners.vertex(i)); - triangles_.push_back(mesh_->facet_corners.vertex(j)); - triangles_.push_back(mesh_->facet_corners.vertex(j + 1)); - } - } - nb_triangles_ = index_t(triangles_.size() / 3); - - // Step 2: get vertices stars - // Step 2.1: get one-ring neighborhood - vector > stars2(mesh_->vertices.nb()); - for(index_t t = 0; t < nb_triangles_; t++) { - stars2[triangles_[3 * t]].push_back(t); - stars2[triangles_[3 * t + 1]].push_back(t); - stars2[triangles_[3 * t + 2]].push_back(t); - } - - // Step 2.2: get two-ring neighborhood - stars_.resize(mesh_->vertices.nb()); - for(index_t i = 0; i < stars2.size(); i++) { - vector Ni; - for(index_t j = 0; j < stars2[i].size(); j++) { - index_t t = stars2[i][j]; - for(index_t iv = 0; iv < 3; iv++) { - index_t v = triangles_[3 * t + iv]; - if(v != i) { - Ni.push_back(v); - } - } - } - sort_unique(Ni); - for(index_t j = 0; j < Ni.size(); j++) { - index_t k = Ni[j]; - stars_[i].insert( - stars_[i].end(), stars2[k].begin(), stars2[k].end() - ); - } - sort_unique(stars_[i]); - } - - // Step 3: create search structure - mesh_vertices_ = Delaunay::create(dimension_, "NN"); - index_t nb_vertices = mesh_->vertices.nb(); - - // TODO: BUG !! mesh_vertices_ keeps a ref. to mesh_vertices - // that is destroyed when leaving this function. - vector mesh_vertices(nb_vertices * dimension_); - for(index_t i = 0; i < nb_vertices; i++) { - for(index_t coord = 0; coord < dimension_; coord++) { - mesh_vertices[i * dimension_ + coord] = - mesh_->vertices.point_ptr(i)[coord]; - } - } - mesh_vertices_->set_vertices(nb_vertices, mesh_vertices.data()); - } - - void project_points_on_surface( - index_t nb_points, double* points, vec3* nearest, bool do_project - ) override { - - prepare_projection(); - - if(use_exact_projection_) { - for(index_t p = 0; p < nb_points; p++) { - Point P(points + p * dimension_); - double d2 = Numeric::max_float64(); - for(index_t t = 0; t < nb_triangles_; t++) { - const Point& p1 = mesh_vertex(triangles_[3 * t]); - const Point& p2 = mesh_vertex(triangles_[3 * t + 1]); - const Point& p3 = mesh_vertex(triangles_[3 * t + 2]); - - double l1, l2, l3; - Point nearestP; - - double cur_d2 = Geom::point_triangle_squared_distance( - P, p1, p2, p3, nearestP, l1, l2, l3 - ); - - if(cur_d2 < d2) { - d2 = cur_d2; - const vec3& p1_R3 = - R3_embedding(triangles_[3 * t]); - const vec3& p2_R3 = - R3_embedding(triangles_[3 * t + 1]); - const vec3& p3_R3 = - R3_embedding(triangles_[3 * t + 2]); - nearest[p] = l1 * p1_R3 + l2 * p2_R3 + l3 * p3_R3; - if(do_project) { - for(coord_index_t - coord = 0; coord < dimension_; coord++) { - (points + p * dimension_)[coord] = - nearestP[coord]; - } - } - } - } - } - return; - } - - // find nearest point on surface in star of nearest vertex - for(index_t p = 0; p < nb_points; p++) { - Point P(points + p * dimension_); - index_t v = mesh_vertices_->nearest_vertex( - points + p * dimension_ - ); - double d2 = Numeric::max_float64(); - nearest[p] = R3_embedding(v); - for(index_t i = 0; i < stars_[v].size(); i++) { - index_t t = stars_[v][i]; - const Point& p1 = mesh_vertex(triangles_[3 * t]); - const Point& p2 = mesh_vertex(triangles_[3 * t + 1]); - const Point& p3 = mesh_vertex(triangles_[3 * t + 2]); - - double l1, l2, l3; - Point nearestP; - double cur_d2 = Geom::point_triangle_squared_distance( - P, p1, p2, p3, nearestP, l1, l2, l3 - ); - if(cur_d2 < d2) { - d2 = cur_d2; - const vec3& p1_R3 = R3_embedding(triangles_[3 * t]); - const vec3& p2_R3 = R3_embedding(triangles_[3 * t + 1]); - const vec3& p3_R3 = R3_embedding(triangles_[3 * t + 2]); - nearest[p] = l1 * p1_R3 + l2 * p2_R3 + l3 * p3_R3; - if(do_project) { - for(coord_index_t coord = 0; - coord < dimension_; coord++ - ) { - (points + p * dimension_)[coord] = - nearestP[coord]; - } - } - } - } - } - } - - /********************************************************************/ - - /** - * \brief Implementation class for computing the restricted Delaunay - * triangulation. - * \details To be used as a template argument - * to RVD::for_each_primal_triangle(). - */ - class GetPrimalTriangles { - public: - /** - * \brief Creates a new GetPrimalTriangles. - * \param[out] triangles where to store the triangles - */ - GetPrimalTriangles( - vector& triangles - ) : - triangles_(triangles) { - } - - /** - * \brief The callback called for each primal triangle. - * \param[in] v1 index of the first vertex - * \param[in] v2 index of the second vertex - * \param[in] v3 index of the third vertex - */ - void operator() (index_t v1, index_t v2, index_t v3) { - triangles_.push_back(v1); - triangles_.push_back(v2); - triangles_.push_back(v3); - } - - private: - vector& triangles_; - }; - - /** - * \brief Implementation class for computing the restricted Delaunay - * triangulation of the connected components. - * - * \details The difference with GetPrimalTriangles is that when a - * restricted Voronoi cell has multiple connected components, - * more triangles are generated to account for the topology. - * To be used as a template argument to RVD::for_each_polygon(). - * The RestrictedVoronoiDiagram needs to be in connected-components - * priority mode. - */ - class GetConnectedComponentsPrimalTriangles { - public: - /** Internal representation of the polygons. */ - typedef typename GenRestrictedVoronoiDiagram::Polygon Polygon; - - /** Internal representation of the vertices. */ - typedef typename GenRestrictedVoronoiDiagram::Vertex Vertex; - - static const index_t UNINITIALIZED = index_t(-1); - static const index_t MULTI_COMP = index_t(-2); - static const index_t ON_BORDER = index_t(-3); - - /** - * \brief Constructs a new GetConnectedComponentsPrimalTriangles. - * \param[in] RVD the restricted Voronoi diagram - * \param[out] triangles where to store the triangles - * \param[out] vertices where to store the vertices - * \param[in] dimension dimension of the restricted Voronoi diagram - * \param[in] mode a combination of constants defined in RDTMode - * \param[in] seed_is_locked specifies for each seed whether it - * can be moved (to RVC centroid or projected on surface). If - * left uninitialized, all the seeds can be moved. - * \param[in] AABB an axis-aligned bounding box tree defined on - * the input surface. It is used if one of (RDT_SELECT_NEAREST, - * RDT_PROJECT_ON_SURFACE) is set in \p mode. If needed and not - * specified, then a new one is created locally. - */ - GetConnectedComponentsPrimalTriangles( - const GenRestrictedVoronoiDiagram& RVD, - vector& triangles, - vector& vertices, - coord_index_t dimension, - RDTMode mode, - const std::vector& seed_is_locked, - MeshFacetsAABB* AABB = nullptr - ) : - RVD_(RVD), - dimension_(dimension), - triangles_(triangles), - vertices_(vertices), - m_(0.0), - cur_seed_(-1), - cur_vertex_(0), - use_RVC_centroids_((mode & RDT_RVC_CENTROIDS) != 0), - select_nearest_((mode & RDT_SELECT_NEAREST) != 0), - project_on_surface_((mode & RDT_PROJECT_ON_SURFACE) != 0), - seed_is_locked_(seed_is_locked), - prefer_seeds_((mode & RDT_PREFER_SEEDS) != 0), - AABB_(AABB) - { - if(prefer_seeds_) { - seed_to_vertex_.assign( - RVD.delaunay()->nb_vertices(),index_t(UNINITIALIZED) - ); - } - } - - /** - * \brief The callback called for each restricted Voronoi cell. - * \param[in] s1 index of current center vertex - * \param[in] P current restricted Voronoi cell - */ - void operator() (index_t s1, const Polygon& P) { - if(RVD_.connected_component_changed()) { - if(cur_seed_ != -1) { - end_connected_component(); - } - begin_connected_component(s1); - } - - if(prefer_seeds_ && !component_on_border_) { - // NOTE: there is one vertex shift between - // adjacent facet and adjacent seed, there - // must be something wrong in the way the - // combinatorial information is initialized, - // to be checked !!! (should be the i index - // for both) - for(index_t i=0; i= 0 && v3 >= 0) { - triangles_.push_back(v1); - triangles_.push_back(index_t(v2)); - triangles_.push_back(index_t(v3)); - } - } - } - } - - /** - * \brief The destructor - */ - ~GetConnectedComponentsPrimalTriangles() { - - bool owns_AABB = false; - if(select_nearest_ || project_on_surface_) { - if(AABB_ == nullptr) { - // Construct an axis-aligned bounding box tree, - // do not reorder the mesh (needs to be pre-reordered) - AABB_ = new MeshFacetsAABB( - *const_cast(RVD_.mesh()),false - ); - owns_AABB = true; - } - } - - if(cur_seed_ != -1) { - end_connected_component(); - } - - if(prefer_seeds_) { - if(select_nearest_) { - for(index_t s=0; svertex_ptr(s); - - - // At this step, vertex_ptr contains - // the centroid of the connected component - // of the RVC, we now determine whether it - // should be replaced by the - // seed (or by a projection onto the surface). - - const double* vertex_ptr = &(vertices_[vbase]); - - // If the seed is nearer to the surface - // than the - // centroid of the connected component of the - // restricted Voronoi cell, then use the seed. - - double seed_dist; - vec3 seed_projection; - double vertex_dist; - vec3 vertex_projection; - - AABB_->nearest_facet( - vec3(seed_ptr), seed_projection, seed_dist - ); - AABB_->nearest_facet( - vec3(vertex_ptr), - vertex_projection, vertex_dist - ); - - if(seed_dist < vertex_dist) { - if(project_on_surface_) { - for( - coord_index_t c = 0; - c < dimension_; ++c - ) { - vertices_[vbase + c] = - seed_projection[c]; - } - } else { - for(coord_index_t c = 0; - c < dimension_; ++c - ) { - vertices_[vbase + c] = seed_ptr[c]; - } - } - } else { - if(project_on_surface_) { - for( - coord_index_t c = 0; - c < dimension_; ++c - ) { - vertices_[vbase + c] = - vertex_projection[c]; - } - } - } - } - } - } else { - - // Current mode: prefer seeds and not select nearest - // Replace all points with the seeds (provided that - // they do not correspond to multiple - // connected components). - - for(index_t s=0; svertex_ptr(s); - - for(coord_index_t c = 0; c < dimension_; ++c) { - vertices_[vbase + c] = seed_ptr[c]; - } - } - } - } - } - - if( - (!prefer_seeds_ || !select_nearest_) && project_on_surface_ - ) { - for(index_t v=0; vnearest_facet(p,q,sq_dist); - vertices_[3*v ] = q.x; - vertices_[3*v+1] = q.y; - vertices_[3*v+2] = q.z; - } - } - - if(owns_AABB) { - delete AABB_; - AABB_ = nullptr; - } - } - - protected: - /** - * \brief Tests whether a given seed is locked. - */ - bool seed_is_locked(index_t s) { - return - seed_is_locked_.size() > 0 && - seed_is_locked_[s] - ; - } - - /** - * \brief Starts a new connected component. - * \param[in] s the seed the connected component - * is associated with. - */ - void begin_connected_component(index_t s) { - cur_seed_ = signed_index_t(s); - for(coord_index_t c = 0; c < dimension_; ++c) { - vertices_.push_back(0.0); - } - m_ = 0.0; - component_on_border_ = false; - } - - /** - * \brief Terminates the current connected component. - */ - void end_connected_component() { - - if( - !use_RVC_centroids_ || - seed_is_locked(index_t(cur_seed_)) || - component_on_border_ - ) { - // Copy seed - index_t vbase = cur_vertex_ * dimension_; - const double* seed_ptr = - RVD_.delaunay()->vertex_ptr(index_t(cur_seed_)); - for(coord_index_t c = 0; c < dimension_; ++c) { - vertices_[vbase + c] = seed_ptr[c]; - } - } else { - // Use restricted Voronoi - // cell component's centroid. - double scal = (m_ < 1e-30 ? 0.0 : 1.0 / m_); - index_t vbase = cur_vertex_ * dimension_; - for(coord_index_t c = 0; c < dimension_; ++c) { - vertices_[vbase + c] *= scal; - } - } - if(prefer_seeds_) { - if(component_on_border_) { - seed_to_vertex_[index_t(cur_seed_)] = ON_BORDER; - } - switch(seed_to_vertex_[index_t(cur_seed_)]) { - case UNINITIALIZED: - seed_to_vertex_[index_t(cur_seed_)] = cur_vertex_; - break; - case ON_BORDER: - break; - default: - seed_to_vertex_[index_t(cur_seed_)] = MULTI_COMP; - break; - } - } - ++cur_vertex_; - } - - private: - const GenRestrictedVoronoiDiagram& RVD_; - coord_index_t dimension_; - vector& triangles_; - vector& vertices_; - double m_; - signed_index_t cur_seed_; - index_t cur_vertex_; - bool use_RVC_centroids_; - bool select_nearest_; - bool project_on_surface_; - const std::vector& seed_is_locked_; - bool prefer_seeds_; - vector seed_to_vertex_; - bool component_on_border_; - MeshFacetsAABB* AABB_; - }; - - /** - * \brief Implementation class for computing the restricted Delaunay - * triangulation in volume mode. - * \details To be used as a template argument - * to RVD::for_each_primal_tetrahedron(). - */ - class GetPrimalTetrahedra { - public: - /** - * \brief Creates a new GetPrimalTetrahedra. - * \param[out] tetrahedra where to store the tetrahedra - */ - GetPrimalTetrahedra( - vector& tetrahedra - ) : - tetrahedra_(tetrahedra) { - } - - /** - * \brief The callback called for each primal tetrahedron. - * \param[in] v1 index of the first vertex - * \param[in] v2 index of the second vertex - * \param[in] v3 index of the third vertex - * \param[in] v4 index of the fourth vertex - */ - void operator() (index_t v1, index_t v2, index_t v3, index_t v4) { - tetrahedra_.push_back(v1); - tetrahedra_.push_back(v2); - tetrahedra_.push_back(v3); - tetrahedra_.push_back(v4); - } - - private: - vector& tetrahedra_; - }; - - void compute_RDT( - vector& simplices, - vector& embedding, - RDTMode mode, - const vector& seed_is_locked, - MeshFacetsAABB* AABB - ) override { - if(volumetric_) { - // For the moment, only simple mode is supported - simplices.clear(); - RVD_.for_each_primal_tetrahedron( - GetPrimalTetrahedra(simplices) - ); - // Reorient the tetrahedra - index_t nb_tetrahedra = simplices.size() / 4; - for(index_t t = 0; t < nb_tetrahedra; ++t) { - const double* p1 = - delaunay()->vertex_ptr(simplices[4 * t]); - const double* p2 = - delaunay()->vertex_ptr(simplices[4 * t + 1]); - const double* - p3 = delaunay()->vertex_ptr(simplices[4 * t + 2]); - const double* - p4 = delaunay()->vertex_ptr(simplices[4 * t + 3]); - if(PCK::orient_3d(p1, p2, p3, p4) < 0) { - std::swap(simplices[4 * t], simplices[4 * t + 1]); - } - } - embedding.clear(); - embedding.reserve(dimension_ * delaunay_->nb_vertices()); - for(index_t i = 0; i < delaunay_->nb_vertices(); i++) { - for(coord_index_t coord = 0; coord < dimension_; coord++) { - embedding.push_back(delaunay_->vertex_ptr(i)[coord]); - } - } - } else { - if((mode & RDT_MULTINERVE) != 0) { - simplices.clear(); - embedding.clear(); - bool sym = RVD_.symbolic(); - RVD_.set_symbolic(true); - RVD_.set_connected_components_priority(true); - RVD_.for_each_polygon( - GetConnectedComponentsPrimalTriangles( - RVD_, simplices, embedding, RVD_.dimension(), - mode, seed_is_locked, AABB - ) - ); - RVD_.set_symbolic(sym); - RVD_.set_connected_components_priority(false); - } else { - // Simple mode: compute RDT, without any post-processing - simplices.clear(); - RVD_.for_each_primal_triangle( - GetPrimalTriangles(simplices) - ); - embedding.clear(); - embedding.reserve(dimension_ * delaunay_->nb_vertices()); - for(index_t i = 0; i < delaunay_->nb_vertices(); i++) { - for( - coord_index_t coord = 0; - coord < dimension_; ++coord - ){ - embedding.push_back( - delaunay_->vertex_ptr(i)[coord] - ); - } - } - } - } - } - - void create_threads() override { - // TODO: check if number of facets is not smaller than - // number of threads - // TODO: create parts even if facets range is specified - // (and subdivide facets range) - if(is_slave_ || facets_begin_ != -1 || facets_end_ != -1) { - return; - } - index_t nb_parts_in = Process::maximum_concurrent_threads(); - if(nb_parts() != nb_parts_in) { - if(nb_parts_in == 1) { - delete_threads(); - } else { - vector facet_ptr; - vector tet_ptr; - mesh_partition( - *mesh_, MESH_PARTITION_HILBERT, - facet_ptr, tet_ptr, nb_parts_in - ); - delete_threads(); - parts_ = new thisclass[nb_parts_in]; - nb_parts_ = nb_parts_in; - for(index_t i = 0; i < nb_parts(); ++i) { - part(i).mesh_ = mesh_; - part(i).set_delaunay(delaunay_); - part(i).R3_embedding_base_ = R3_embedding_base_; - part(i).R3_embedding_stride_ = R3_embedding_stride_; - part(i).has_weights_ = has_weights_; - part(i).master_ = this; - part(i).RVD_.set_mesh(mesh_); - part(i).set_facets_range( - facet_ptr[i], facet_ptr[i + 1] - ); - part(i).set_exact_predicates(RVD_.exact_predicates()); - part(i).set_volumetric(volumetric()); - part(i).set_check_SR(RVD_.check_SR()); - } - if(mesh_->cells.nb() != 0) { - for(index_t i = 0; i < nb_parts(); ++i) { - part(i).set_tetrahedra_range( - tet_ptr[i], tet_ptr[i + 1] - ); - } - } - geo_assert(!Process::is_running_threads()); - } - } - } - - void set_volumetric(bool x) override { - volumetric_ = x; - for(index_t i = 0; i < nb_parts(); ++i) { - part(i).set_volumetric(x); - } - } - - void set_facets_range( - index_t facets_begin, index_t facets_end - ) override { - RVD_.set_facets_range(facets_begin, facets_end); - facets_begin_ = signed_index_t(facets_begin); - facets_end_ = signed_index_t(facets_end); - } - - void set_tetrahedra_range( - index_t tets_begin, index_t tets_end - ) override { - RVD_.set_tetrahedra_range(tets_begin, tets_end); - tets_begin_ = signed_index_t(tets_begin); - tets_end_ = signed_index_t(tets_end); - } - - void delete_threads() override { - delete[] parts_; - parts_ = nullptr; - nb_parts_ = 0; - } - - /** - * \brief Gets the number of parts (or number of threads). - */ - index_t nb_parts() const { - return nb_parts_; - } - - /** - * \brief Gets a given part from its index. - * \param[in] i index of the part - * \pre \p i < nb_parts() - */ - thisclass& part(index_t i) { - geo_debug_assert(i < nb_parts()); - return parts_[i]; - } - - /** - * \copydoc RestrictedVoronoiDiagram::point_allocator() - */ - GEOGen::PointAllocator* point_allocator() override { - return RVD_.point_allocator(); - } - - - protected: - - GenRestrictedVoronoiDiagram RVD_; - - // For projection - bool use_exact_projection_; - index_t nb_triangles_; - vector triangles_; - vector > stars_; - Delaunay_var mesh_vertices_; - - // One of MT_NONE, MT_LLOYD, MT_NEWTON - ThreadMode thread_mode_; - - bool is_slave_; - - // Variables for 'master' in multithreading mode - thisclass* parts_; - index_t nb_parts_; - Process::SpinLockArray spinlocks_; - - // Newton mode with int. simplex - IntegrationSimplex* simplex_func_; - - // PolygonCallback mode. - RVDPolygonCallback* polygon_callback_; - - // PolyhedronCallback mode. - RVDPolyhedronCallback* polyhedron_callback_; - - // master stores argument for compute_centroids() and - // compute_CVT_func_grad() to pass it to the parts. - double* arg_vectors_; - double* arg_scalars_; - - // Variables for 'slaves' in multithreading mode - thisclass* master_; - double funcval_; // Newton mode: function value - - protected: - /** - * \brief Destructor - */ - ~RVD_Nd_Impl() override { - delete_threads(); - } - - private: - /** \brief Forbids construction by copy. */ - RVD_Nd_Impl(const thisclass&); - - /** \brief Forbids assignment. */ - thisclass& operator= (const thisclass&); - }; -} - -/****************************************************************************/ - -namespace GEO { - - RestrictedVoronoiDiagram* RestrictedVoronoiDiagram::create( - Delaunay* delaunay, Mesh* mesh, - const double* R3_embedding, index_t R3_embedding_stride - ) { - - geo_cite("DBLP:journals/tog/EdelsbrunnerM90"); - geo_cite("DBLP:conf/compgeom/Shewchuk96"); - geo_cite("meyer:inria-00344297"); - geo_cite("DBLP:conf/gmp/YanWLL10"); - geo_cite("DBLP:journals/cad/YanWLL13"); - geo_cite("DBLP:journals/cad/Levy16"); - - delaunay->set_stores_neighbors(true); - RestrictedVoronoiDiagram* result = nullptr; - geo_assert(delaunay != nullptr); - coord_index_t dim = delaunay->dimension(); - switch(dim) { - case 2: - result = new RVD_Nd_Impl<2>( - delaunay, mesh, R3_embedding, R3_embedding_stride - ); - break; - case 3: - result = new RVD_Nd_Impl<3>( - delaunay, mesh, R3_embedding, R3_embedding_stride - ); - break; - case 4: - result = new RVD_Nd_Impl<4>( - delaunay, mesh, R3_embedding, R3_embedding_stride - ); - break; - case 6: - result = new RVD_Nd_Impl<6>( - delaunay, mesh, R3_embedding, R3_embedding_stride - ); - break; - case 8: - result = new RVD_Nd_Impl<8>( - delaunay, mesh, R3_embedding, R3_embedding_stride - ); - break; - default: - geo_assert_not_reached; - } - if(CmdLine::get_arg("algo:predicates") == "exact") { - result->set_exact_predicates(true); - } - return result; - } - - void RestrictedVoronoiDiagram::set_delaunay(Delaunay* delaunay) { - delaunay_ = delaunay; - if(delaunay_ != nullptr) { - dimension_ = delaunay->dimension(); - } else { - dimension_ = 0; - } - } - - RestrictedVoronoiDiagram::~RestrictedVoronoiDiagram() { - } - - RestrictedVoronoiDiagram::RestrictedVoronoiDiagram( - Delaunay* delaunay, Mesh* mesh, - const double* R3_embedding, index_t R3_embedding_stride - ) : - dimension_(0), - mesh_(mesh), - R3_embedding_base_(R3_embedding), - R3_embedding_stride_(R3_embedding_stride) { - set_delaunay(delaunay); - has_weights_ = false; - facets_begin_ = -1; - facets_end_ = -1; - tets_begin_ = -1; - tets_end_ = -1; - volumetric_ = false; - } - - - void RestrictedVoronoiDiagram::compute_RDT( - Mesh& RDT, - RDTMode mode, - const vector& seed_is_locked, - MeshFacetsAABB* AABB - ) { - vector simplices; - vector embedding; - compute_RDT( - simplices, embedding, - mode, seed_is_locked, - AABB - ); - if(volumetric()) { - RDT.cells.assign_tet_mesh(dimension(),embedding,simplices,true); - } else { - RDT.facets.assign_triangle_mesh( - dimension(),embedding,simplices,true - ); - if((mode & RDT_DONT_REPAIR) == 0) { - mesh_repair(RDT); // Needed to reorient triangles - } - } - } - - -} - diff --git a/src/lib/geogram/voronoi/RVD.cpp.old b/src/lib/geogram/voronoi/RVD.cpp.old deleted file mode 100644 index c3942a43..00000000 --- a/src/lib/geogram/voronoi/RVD.cpp.old +++ /dev/null @@ -1,2651 +0,0 @@ -/* - * Copyright (c) 2012-2014, Bruno Levy - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * * Neither the name of the ALICE Project-Team nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * If you modify this software, you should include a notice giving the - * name of the person performing the modification, the date of modification, - * and the reason for such modification. - * - * Contact: Bruno Levy - * - * Bruno.Levy@inria.fr - * http://www.loria.fr/~levy - * - * ALICE Project - * LORIA, INRIA Lorraine, - * Campus Scientifique, BP 239 - * 54506 VANDOEUVRE LES NANCY CEDEX - * FRANCE - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * There are three levels of implementation: - * Level 1: RestrictedVoronoiDiagram is the abstract API seen from client code - * Level 2: RVD_Nd_Impl implements RestrictedVoronoiDiagram - * Level 3: RVD_Nd_Impl::GenRestrictedVoronoiDiagram is - * an instantiation of GEOGen::RestrictedVoronoiDiagram (from generic_RVD.h) - * - * Warning: there are approx. 1000 lines of boring code ahead. - */ - -namespace { - - using namespace GEO; - - /** - * \brief Generic implementation of RestrictedVoronoiDiagram. - * \tparam DIM dimension - */ - template - class RVD_Nd_Impl : public GEO::RestrictedVoronoiDiagram { - - /** \brief This class type */ - typedef RVD_Nd_Impl thisclass; - - /** \brief The base class of this class */ - typedef RestrictedVoronoiDiagram baseclass; - - public: - /** \brief Implementation based on the generic version. */ - typedef GEOGen::RestrictedVoronoiDiagram - GenRestrictedVoronoiDiagram; - - /** \brief Representation of points. */ - typedef vecng Point; - - /** \brief Representation of vectors. */ - typedef vecng Vector; - - /** \brief Represents a point and its symbolic information. */ - typedef typename GenRestrictedVoronoiDiagram::Vertex Vertex; - - /** - * \brief Specifies the computation done by the threads. - */ - enum ThreadMode { - MT_NONE, /**< uninitialized */ - MT_LLOYD, /**< Lloyd iteration */ - MT_NEWTON, /**< Newton optimization */ - MT_INT_SMPLX, /**< Newton with integration simplex */ - MT_POLYG, /**< Polygon callback */ - MT_POLYH /**< Polyhedron callback */ - }; - - /** - * \brief Gets a mesh vertex from its index. - * \param[in] v index of the vertex - * \return a const reference to a Point - */ - const Point& mesh_vertex(index_t v) { - return *(const Point*) mesh_->vertices.point_ptr(v); - } - - - /** - * \brief Creates a RVD_Nd_Impl. - * - * \details The dimension is determined by \p mesh->dimension(). - * \param[in] delaunay the Delaunay triangulation - * \param[in] mesh the input mesh - * \param[in] R3_embedding gives for each vertex - * its mapping in 3D space. - * \param[in] R3_embedding_stride gives the stride between - * two consecutive vertices in R3_embedding - */ - RVD_Nd_Impl( - Delaunay* delaunay, Mesh* mesh, - const double* R3_embedding, index_t R3_embedding_stride - ) : - RestrictedVoronoiDiagram( - delaunay, mesh, R3_embedding, R3_embedding_stride - ), - RVD_(delaunay, mesh) { - use_exact_projection_ = false; - is_slave_ = false; - master_ = nullptr; - has_weights_ = false; - if(mesh->vertices.attributes().is_defined("weight")) { - vertex_weight_.bind(mesh->vertices.attributes(), "weight"); - has_weights_ = true; - } - parts_ = nullptr; - nb_parts_ = 0; - funcval_ = 0.0; - simplex_func_ = nullptr; - polygon_callback_ = nullptr; - polyhedron_callback_ = nullptr; - arg_vectors_ = nullptr; - arg_scalars_ = nullptr; - thread_mode_ = MT_NONE; - nb_triangles_ = 0; - } - - /** - * \brief Constructor for parts, used in multithreading mode. - */ - RVD_Nd_Impl() : - RestrictedVoronoiDiagram(nullptr, nullptr, nullptr, 0), - RVD_(nullptr, nullptr) { - use_exact_projection_ = false; - is_slave_ = true; - master_ = nullptr; - mesh_ = nullptr; - parts_ = nullptr; - nb_parts_ = 0; - facets_begin_ = -1; - facets_end_ = -1; - funcval_ = 0.0; - simplex_func_ = nullptr; - polygon_callback_ = nullptr; - polyhedron_callback_ = nullptr; - arg_vectors_ = nullptr; - arg_scalars_ = nullptr; - thread_mode_ = MT_NONE; - nb_triangles_ = 0; - } - - void set_delaunay(Delaunay* delaunay) override { - baseclass::set_delaunay(delaunay); - RVD_.set_delaunay(delaunay); - for(index_t p = 0; p < nb_parts_; ++p) { - parts_[p].set_delaunay(delaunay); - } - } - - void set_check_SR(bool x) override { - RVD_.set_check_SR(x); - for(index_t p = 0; p < nb_parts_; ++p) { - parts_[p].set_check_SR(x); - } - } - - void set_exact_predicates(bool x) override { - RVD_.set_exact_predicates(x); - for(index_t p = 0; p < nb_parts_; ++p) { - parts_[p].set_exact_predicates(x); - } - } - - bool exact_predicates() const override { - return RVD_.exact_predicates(); - } - - /********************************************************************/ - - /** - * \brief Place holder, "no locking" policy. - * \details NoLocks is used by algorithms templated - * by locking policy, for the single-threaded instances - * that do not need synchronization. The multi-threaded - * instances are parameterized by SpinLockArray. - */ - class NoLocks { - public: - /** - * \brief Acquires a spinlock. - * \details Does nothing in this version - * \param[in] i index of the spinlock to acquire - */ - void acquire_spinlock(index_t i) { - geo_argused(i); - } - - /** - * \brief Releases a spinlock. - * \details Does nothing in this version - * \param[in] i index of the spinlock to release - */ - void release_spinlock(index_t i) { - geo_argused(i); - } - }; - - // ____________________________________________________________________ - - /** - * \brief Implementation class for surfacic Lloyd relaxation. - * \details To be used as a template argument - * to RVD::for_each_triangle(). - * This version ignores the weights. - * - * Computes for each RVD cell: - * - mg[v] (v's Voronoi cell's total area times centroid) - * - m[v] (v's total area) - * \tparam LOCKS locking policy - * (can be one of Process::SpinLockArray, NoLocks) - */ - template - class ComputeCentroids { - public: - /** - * \brief Constructs a ComputeCentroids. - * \param[out] mg where to store the centroids - * \param[out] m where to store the masses - * \param[in] locks the array of locks - * (or NoLocks in single thread mode) - */ - ComputeCentroids( - double* mg, - double* m, - LOCKS& locks - ) : - mg_(mg), - m_(m), - locks_(locks) { - } - - /** - * \brief The callback called for each integration simplex. - * \param[in] v index of current center vertex - * \param[in] p1 first vertex of current integration simplex - * \param[in] p2 second vertex of current integration simplex - * \param[in] p3 third vertex of current integration simplex - */ - void operator() ( - index_t v, - const double* p1, - const double* p2, - const double* p3 - ) const { - double cur_m = Geom::triangle_area(p1, p2, p3, DIM); - double s = cur_m / 3.0; - locks_.acquire_spinlock(v); - m_[v] += cur_m; - double* cur_mg_out = mg_ + v * DIM; - for(coord_index_t coord = 0; coord < DIM; coord++) { - cur_mg_out[coord] += - s * (p1[coord] + p2[coord] + p3[coord]); - } - locks_.release_spinlock(v); - } - - private: - double* mg_; - double* m_; - LOCKS& locks_; - }; - - /** - * \brief Implementation class for surfacic Lloyd relaxation. - * \details To be used as a template - * argument to RVD::for_each_triangle(). - * This version takes the weights into account. - * - * Computes for each RVD cell: - * - mg[v] (v's Voronoi cell's total area times centroid) - * - m[v] (v's total area) - * \tparam LOCKS locking policy - * (can be one of Process::SpinLockArray, NoLocks) - */ - template - class ComputeCentroidsWeighted { - public: - /** - * \brief Constructs a ComputeCentroidsWeighted. - * \param[out] mg where to store the centroids - * \param[out] m where to store the masses - * \param[in] locks the array of locks - * (or NoLocks in single thread mode) - */ - ComputeCentroidsWeighted( - double* mg, - double* m, - LOCKS& locks - ) : - mg_(mg), - m_(m), - locks_(locks) { - } - - /** - * \brief The callback called for each integration simplex. - * \param[in] v index of current center vertex - * \param[in] v1 first vertex of current integration simplex - * \param[in] v2 second vertex of current integration simplex - * \param[in] v3 third vertex of current integration simplex - */ - void operator() ( - index_t v, - const Vertex& v1, - const Vertex& v2, - const Vertex& v3 - ) const { - double cur_m; - double cur_Vg[DIM]; - Geom::triangle_centroid( - v1.point(), v2.point(), v3.point(), - v1.weight(), v2.weight(), v3.weight(), - cur_Vg, cur_m, DIM - ); - locks_.acquire_spinlock(v); - m_[v] += cur_m; - double* cur_mg_out = mg_ + v * DIM; - for(coord_index_t coord = 0; coord < DIM; coord++) { - cur_mg_out[coord] += cur_Vg[coord]; - } - locks_.release_spinlock(v); - } - - private: - double* mg_; - double* m_; - LOCKS& locks_; - }; - - void compute_centroids_on_surface(double* mg, double* m) override { - create_threads(); - if(nb_parts() == 0) { - if(master_ != nullptr) { - if(has_weights_) { - RVD_.for_each_triangle( - ComputeCentroidsWeighted( - mg, m, master_->spinlocks_ - ) - ); - } else { - RVD_.for_each_triangle( - ComputeCentroids( - mg, m, master_->spinlocks_ - ) - ); - } - } else { - NoLocks nolocks; - if(has_weights_) { - RVD_.for_each_triangle( - ComputeCentroidsWeighted( - mg, m, nolocks - ) - ); - } else { - RVD_.for_each_triangle( - ComputeCentroids(mg, m, nolocks) - ); - } - } - } else { - thread_mode_ = MT_LLOYD; - arg_vectors_ = mg; - arg_scalars_ = m; - spinlocks_.resize(delaunay_->nb_vertices()); - parallel_for( - 0, nb_parts(), - parallel_for_member_callback(this, &thisclass::run_thread) - ); - } - } - - /********************************************************************/ - - /** - * \brief Implementation class for surfacic Lloyd relaxation. - * \details To be used as a template argument - * to RVD::for_each_volumetric_integration_simplex(). - * This version ignores the weights. - * - * Computes for each RVD cell: - * - mg[v] (v's Voronoi cell's total area times centroid) - * - m[v] (v's total area) - * \tparam LOCKS locking policy - * (can be one of Process::SpinLockArray, NoLocks) - */ - template - class ComputeCentroidsVolumetric { - public: - /** - * \brief Constructs a ComputeCentroidsVolumetric. - * \param[out] mg where to store the centroids - * \param[out] m where to store the masses - * \param[in] delaunay the Delaunay triangulation - * \param[in] locks the array of locks - * (or NoLocks in single thread mode) - */ - ComputeCentroidsVolumetric( - double* mg, - double* m, - const Delaunay* delaunay, - LOCKS& locks - ) : - mg_(mg), - m_(m), - delaunay_(delaunay), - locks_(locks) { - } - - /** - * \brief The callback called for each integration simplex. - * \param[in] v index of current center vertex - * \param[in] v_adj (unused here) is the index of the Voronoi cell - * adjacent to t accros facet (\p v1, \p v2, \p v3) or - * -1 if it does not exists - * \param[in] t (unused here) is the index of the current - * tetrahedron - * \param[in] t_adj (unused here) is the index of the - * tetrahedron adjacent to t accros facet (\p v1, \p v2, \p v3) - * or -1 if it does not exists - * \param[in] p0 first vertex of current integration simplex - * \param[in] p1 second vertex of current integration simplex - * \param[in] p2 third vertex of current integration simplex - * \param[in] p3 fourth vertex of current integration simplex - */ - void operator() ( - index_t v, signed_index_t v_adj, - index_t t, signed_index_t t_adj, - const double* p0, - const double* p1, - const double* p2, - const double* p3 - ) const { - geo_argused(v_adj); - geo_argused(t); - geo_argused(t_adj); - double cur_m = Geom::tetra_volume( - p0, p1, p2, p3 - ); - double s = cur_m / 4.0; - locks_.acquire_spinlock(v); - m_[v] += cur_m; - double* cur_mg_out = mg_ + v * DIM; - for(coord_index_t coord = 0; coord < DIM; coord++) { - cur_mg_out[coord] += s * ( - p0[coord] + p1[coord] + p2[coord] + p3[coord] - ); - } - locks_.release_spinlock(v); - } - - private: - double* mg_; - double* m_; - const Delaunay* delaunay_; - LOCKS& locks_; - }; - - void compute_centroids_in_volume(double* mg, double* m) override { - create_threads(); - if(nb_parts() == 0) { - if(master_ != nullptr) { - RVD_.for_each_tetrahedron( - ComputeCentroidsVolumetric( - mg, m, RVD_.delaunay(), master_->spinlocks_ - ) - ); - } else { - NoLocks nolocks; - RVD_.for_each_tetrahedron( - ComputeCentroidsVolumetric( - mg, m, RVD_.delaunay(), nolocks - ) - ); - } - } else { - thread_mode_ = MT_LLOYD; - arg_vectors_ = mg; - arg_scalars_ = m; - spinlocks_.resize(delaunay_->nb_vertices()); - parallel_for( - 0, nb_parts(), - parallel_for_member_callback(this, &thisclass::run_thread) - ); - } - } - - /********************************************************************/ - - /** - * \brief Implementation class for Newton-based restricted CVT. - * \details To be used as a template argument - * to RVD::for_each_triangle(). - * This version ignores the weights. - * - * Computes for each RVD cell: - * - g (gradient) - * - f (CVT energy) - * \tparam LOCKS locking policy - * (can be one of Process::SpinLockArray, NoLocks) - */ - template - class ComputeCVTFuncGrad { - public: - /** - * \brief Constructs a ComputeCVTFuncGrad. - * \param[in] RVD the restricted Voronoi diagram - * \param[out] f the computed function value - * \param[out] g the computed gradient of f, - * allocated by caller, and managed - * by caller - * \param[in] locks the array of locks - * (or NoLocks in single thread mode) - */ - ComputeCVTFuncGrad( - const GenRestrictedVoronoiDiagram& RVD, - double& f, - double* g, - LOCKS& locks - ) : - f_(f), - g_(g), - locks_(locks), - RVD_(RVD) { - } - - /** - * \brief The callback called for each integration simplex. - * \param[in] v index of current center vertex - * \param[in] p1 first vertex of current integration simplex - * \param[in] p2 second vertex of current integration simplex - * \param[in] p3 third vertex of current integration simplex - */ - void operator() ( - index_t v, - const double* p1, - const double* p2, - const double* p3 - ) const { - - const double* p0 = RVD_.delaunay()->vertex_ptr(v); - - double t_area = Geom::triangle_area(p1, p2, p3, DIM); - - double cur_f = 0.0; - for(index_t c = 0; c < DIM; c++) { - double u0 = p0[c] - p1[c]; - double u1 = p0[c] - p2[c]; - double u2 = p0[c] - p3[c]; - cur_f += u0 * u0; - cur_f += u1 * (u0 + u1); - cur_f += u2 * (u0 + u1 + u2); - } - - f_ += t_area * cur_f / 6.0; - - locks_.acquire_spinlock(v); - for(index_t c = 0; c < DIM; c++) { - double Gc = (1.0 / 3.0) * (p1[c] + p2[c] + p3[c]); - g_[DIM * v + c] += (2.0 * t_area) * (p0[c] - Gc); - } - locks_.release_spinlock(v); - } - - double& f_; - double* g_; - LOCKS& locks_; - const GenRestrictedVoronoiDiagram& RVD_; - }; - - /** - * \brief Implementation class for Newton-based restricted CVT. - * \details To be used as a template argument - * to RVD::for_each_triangle(). - * This version takes the weights into account. - * - * Computes for each RVD cell: - * - g (gradient) - * - f (CVT energy) - * \tparam LOCKS locking policy - * (can be one of Process::SpinLockArray, NoLocks) - */ - template - class ComputeCVTFuncGradWeighted { - public: - /** - * \brief Constructs a ComputeCVTFuncGradWeighted. - * \param[in] RVD the restricted Voronoi diagram - * \param[out] f the computed function value - * \param[out] g the computed gradient of f, - * allocated by caller, and managed by caller - * \param[in] locks the array of locks - * (or NoLocks in single thread mode) - */ - ComputeCVTFuncGradWeighted( - const GenRestrictedVoronoiDiagram& RVD, - double& f, - double* g, - LOCKS& locks - ) : - f_(f), - g_(g), - locks_(locks), - RVD_(RVD) { - } - - /** - * \brief The callback called for each integration simplex. - * \param[in] v index of current center vertex - * \param[in] v1 first vertex of current integration simplex - * \param[in] v2 second vertex of current integration simplex - * \param[in] v3 third vertex of current integration simplex - */ - void operator() ( - index_t v, - const Vertex& v1, - const Vertex& v2, - const Vertex& v3 - ) const { - - const double* p0 = RVD_.delaunay()->vertex_ptr(v); - - const double* p1 = v1.point(); - const double* p2 = v2.point(); - const double* p3 = v3.point(); - - double t_area = Geom::triangle_area(p1, p2, p3, DIM); - - double Sp = v1.weight() + v2.weight() + v3.weight(); - double rho[3], alpha[3]; - rho[0] = v1.weight(); - rho[1] = v2.weight(); - rho[2] = v3.weight(); - alpha[0] = Sp + rho[0]; - alpha[1] = Sp + rho[1]; - alpha[2] = Sp + rho[2]; - - double dotprod_00 = 0.0; - double dotprod_10 = 0.0; - double dotprod_11 = 0.0; - double dotprod_20 = 0.0; - double dotprod_21 = 0.0; - double dotprod_22 = 0.0; - for(unsigned int c = 0; c < DIM; c++) { - double sp0 = p0[c] - p1[c]; - double sp1 = p0[c] - p2[c]; - double sp2 = p0[c] - p3[c]; - dotprod_00 += sp0 * sp0; - dotprod_10 += sp1 * sp0; - dotprod_11 += sp1 * sp1; - dotprod_20 += sp2 * sp0; - dotprod_21 += sp2 * sp1; - dotprod_22 += sp2 * sp2; - } - - double cur_f = 0.0; - cur_f += (alpha[0] + rho[0]) * dotprod_00; // 0 0 - cur_f += (alpha[1] + rho[0]) * dotprod_10; // 1 0 - cur_f += (alpha[1] + rho[1]) * dotprod_11; // 1 1 - cur_f += (alpha[2] + rho[0]) * dotprod_20; // 2 0 - cur_f += (alpha[2] + rho[1]) * dotprod_21; // 2 1 - cur_f += (alpha[2] + rho[2]) * dotprod_22; // 2 2 - - f_ += t_area * cur_f / 30.0; - double* g_out = g_ + v * DIM; - locks_.acquire_spinlock(v); - for(index_t c = 0; c < DIM; c++) { - g_out[c] += (t_area / 6.0) * ( - 4.0 * Sp * p0[c] - ( - alpha[0] * p1[c] + - alpha[1] * p2[c] + - alpha[2] * p3[c] - ) - ); - } - locks_.release_spinlock(v); - } - - double& f_; - double* g_; - LOCKS& locks_; - const GenRestrictedVoronoiDiagram& RVD_; - }; - - void compute_CVT_func_grad_on_surface(double& f, double* g) override { - create_threads(); - if(nb_parts() == 0) { - if(master_ != nullptr) { - if(has_weights_) { - RVD_.for_each_triangle( - ComputeCVTFuncGradWeighted( - RVD_, f, g, master_->spinlocks_ - ) - ); - } else { - RVD_.for_each_triangle( - ComputeCVTFuncGrad( - RVD_, f, g, master_->spinlocks_ - ) - ); - } - } else { - NoLocks nolocks; - if(has_weights_) { - RVD_.for_each_triangle( - ComputeCVTFuncGradWeighted( - RVD_, f, g, nolocks - ) - ); - } else { - RVD_.for_each_triangle( - ComputeCVTFuncGrad( - RVD_, f, g, nolocks - ) - ); - } - } - } else { - thread_mode_ = MT_NEWTON; - arg_vectors_ = g; - spinlocks_.resize(delaunay_->nb_vertices()); - for(index_t t = 0; t < nb_parts(); t++) { - part(t).funcval_ = 0.0; - } - parallel_for( - 0, nb_parts(), - parallel_for_member_callback(this, &thisclass::run_thread) - ); - for(index_t t = 0; t < nb_parts(); t++) { - f += part(t).funcval_; - } - } - } - - /********************************************************************/ - - /** - * \brief Implementation class for Newton-based restricted CVT - * in volume. - * \details To be used as a template argument - * to RVD::for_each_volumetric_integration_simplex(). - * This version ignores the weights. - * - * Computes for each RVD cell: - * - g (gradient) - * - f (CVT energy) - * \tparam LOCKS locking policy - * (can be one of Process::SpinLockArray, NoLocks) - */ - template - class ComputeCVTFuncGradVolumetric { - public: - /** - * \brief Constructs a ComputeCentroidsFuncGradVolumetric. - * \param[in] RVD the restricted Voronoi diagram - * \param[out] f the computed function value - * \param[out] g the computed gradient of f, - * allocated by caller, and managed - * by caller - * \param[in] locks the array of locks - * (or NoLocks in single thread mode) - */ - ComputeCVTFuncGradVolumetric( - const GenRestrictedVoronoiDiagram& RVD, - double& f, - double* g, - LOCKS& locks - ) : - f_(f), - g_(g), - locks_(locks), - RVD_(RVD) { - } - - /** - * \brief The callback called for each integration simplex. - * \param[in] v index of current center vertex - * \param[in] v_adj (unused here) is the index of the Voronoi cell - * adjacent to t accros facet (\p v1, \p v2, \p v3) or - * -1 if it does not exists - * \param[in] t (unused here) is the index of the current - * tetrahedron - * \param[in] t_adj (unused here) is the index of the - * tetrahedron adjacent to t accros facet (\p v1, \p v2, \p v3) - * or -1 if it does not exists - * \param[in] p1 first vertex of current integration simplex - * \param[in] p2 second vertex of current integration simplex - * \param[in] p3 third vertex of current integration simplex - */ - void operator() ( - index_t v, - signed_index_t v_adj, - index_t t, - signed_index_t t_adj, - const double* p1, - const double* p2, - const double* p3 - ) const { - geo_argused(v_adj); - geo_argused(t); - geo_argused(t_adj); - const double* p0 = RVD_.delaunay()->vertex_ptr(v); - - double mi = Geom::tetra_volume(p0, p1, p2, p3); - - // fi = (mi/10)*(U.U + V.V + W.W + U.V + V.W + W.U) - // where: U = p1-p0 ; V = p2-p0 and W=p3-p0 - double fi = 0.0; - for(coord_index_t c = 0; c < DIM; ++c) { - double Uc = p1[c] - p0[c]; - double Vc = p2[c] - p0[c]; - double Wc = p3[c] - p0[c]; - fi += geo_sqr(Uc) + geo_sqr(Vc) + geo_sqr(Wc); - fi += (Uc * Vc + Vc * Wc + Wc * Uc); - } - fi *= (mi / 10.0); - f_ += fi; - - // gi = 2*mi(p0 - 1/4(p0 + p1 + p2 + p3)) - double* g_out = g_ + v * DIM; - locks_.acquire_spinlock(v); - for(coord_index_t c = 0; c < DIM; ++c) { - g_out[c] += 2.0 * mi * ( - 0.75 * p0[c] - - 0.25 * p1[c] - 0.25 * p2[c] - 0.25 * p3[c] - ); - } - locks_.release_spinlock(v); - } - - double& f_; - double* g_; - LOCKS& locks_; - const GenRestrictedVoronoiDiagram& RVD_; - }; - - void compute_CVT_func_grad_in_volume(double& f, double* g) override { - create_threads(); - if(nb_parts() == 0) { - if(master_ != nullptr) { - RVD_.for_each_volumetric_integration_simplex( - ComputeCVTFuncGradVolumetric( - RVD_, f, g, master_->spinlocks_ - ) - ); - } else { - NoLocks nolocks; - RVD_.for_each_volumetric_integration_simplex( - ComputeCVTFuncGradVolumetric( - RVD_, f, g, nolocks - ) - ); - } - } else { - thread_mode_ = MT_NEWTON; - arg_vectors_ = g; - spinlocks_.resize(delaunay_->nb_vertices()); - for(index_t t = 0; t < nb_parts(); t++) { - part(t).funcval_ = 0.0; - } - parallel_for( - 0, nb_parts(), - parallel_for_member_callback(this, &thisclass::run_thread) - ); - for(index_t t = 0; t < nb_parts(); t++) { - f += part(t).funcval_; - } - } - } - - /********************************************************************/ - - /** - * \brief Implementation class for computing function integrals - * and gradients over integration simplices. - * \details To be used as a template argument - * to RVD::for_each_triangle() and - * RVD::for_each_volumetric_integration_simplex() - */ - class ComputeCVTFuncGradIntegrationSimplex { - public: - /** - * \brief Constructs a ComputeCVTFuncGradIntegrationSimplex. - * \param[in] RVD the Restricted Voronoi Diagram - * \param[in] F the IntegrationSimplex - */ - ComputeCVTFuncGradIntegrationSimplex( - const GenRestrictedVoronoiDiagram& RVD, - IntegrationSimplex* F - ) : - f_(0.0), - RVD_(RVD), - simplex_func_(F) { - simplex_func_->reset_thread_local_storage(); - } - - /** - * \brief The callback called for each surfacic integration simplex. - * \param[in] i index of current center vertex - * \param[in] v1 first vertex of current integration simplex - * \param[in] v2 second vertex of current integration simplex - * \param[in] v3 third vertex of current integration simplex - */ - void operator() ( - index_t i, - const Vertex& v1, - const Vertex& v2, - const Vertex& v3 - ) { - f_ += simplex_func_->eval( - i,v1,v2,v3,RVD_.current_facet() - ); - } - - /** - * \brief The callback called for each volumetric - * integration simplex. - * \param[in] v index of current center vertex - * \param[in] v_adj index of the Voronoi cell adjacent to t accros - * facet (\p v1, \p v2, \p v3) or -1 if it does not exists - * \param[in] t index of the current tetrahedron - * \param[in] t_adj index of the tetrahedron adjacent to t accros - * facet (\p v1, \p v2, \p v3) or -1 if it does not exists - * \param[in] v1 first vertex of current integration simplex - * \param[in] v2 second vertex of current integration simplex - * \param[in] v3 third vertex of current integration simplex - */ - void operator() ( - index_t v, - signed_index_t v_adj, - index_t t, - signed_index_t t_adj, - const Vertex& v1, - const Vertex& v2, - const Vertex& v3 - ) { - geo_argused(v_adj); - geo_argused(t_adj); - f_ += simplex_func_->eval( - v,v1,v2,v3,t,index_t(t_adj),index_t(v_adj) - ); - } - - /** - * \brief Gets the function value. - * \return The function value accumulated so far. - */ - double f() const { - return f_; - } - - private: - double f_; - const GenRestrictedVoronoiDiagram& RVD_; - IntegrationSimplex* simplex_func_; - }; - - void compute_integration_simplex_func_grad( - double& f, double* g, IntegrationSimplex* F - ) override { - create_threads(); - if(nb_parts() == 0) { - if(master_ == nullptr) { - F->set_points_and_gradient( - delaunay()->dimension(), - delaunay()->nb_vertices(), - delaunay()->vertex_ptr(0), - g - ); - } - ComputeCVTFuncGradIntegrationSimplex C(RVD_,F); - bool sym = RVD_.symbolic(); - RVD_.set_symbolic(true); - if(F->volumetric()) { - RVD_.for_each_volumetric_integration_simplex( - C, - F->background_mesh_has_varying_attribute(), - false /* Coherent triangles */ - ); - } else { - RVD_.for_each_triangle(C); - } - RVD_.set_symbolic(sym); - funcval_ = C.f(); - f = C.f(); - } else { - spinlocks_.resize(delaunay_->nb_vertices()); - F->set_points_and_gradient( - delaunay()->dimension(), - delaunay()->nb_vertices(), - delaunay()->vertex_ptr(0), - g, - &spinlocks_ - ); - thread_mode_ = MT_INT_SMPLX; - arg_vectors_ = g; - simplex_func_ = F; - funcval_ = 0.0; - for(index_t t = 0; t < nb_parts(); t++) { - part(t).arg_vectors_ = g; - part(t).simplex_func_ = F; - part(t).funcval_ = 0.0; - } - - parallel_for( - 0, nb_parts(), - parallel_for_member_callback(this, &thisclass::run_thread) - ); - - f = 0.0; - for(index_t t = 0; t < nb_parts(); t++) { - f += part(t).funcval_; - } - } - } - - /********************************************************************/ - - /** - * \brief Adapter class used internally to implement for_each_polygon() - * \details Gets the current triangle from the RVD and passes it back - * to the callback. It is needed because GenericRVD::for_each_polygon() - * does not pass the current triangle. - */ - // TODO: pass it through all the callbacks, because it is ridiculous: - // we pass it through the first levels, then throw it, then retrieve it - // (see GenRVD) - class PolygonCallbackAction { - public: - /** - * \brief PolygonCallbackAction constructor - * \param[in] RVD a pointer to the restricted Voronoi diagram - * \param[in] callback a pointer to the PolygonCallback - */ - PolygonCallbackAction( - GenRestrictedVoronoiDiagram& RVD, - GEO::RVDPolygonCallback& callback - ) : - RVD_(RVD), - callback_(callback) { - } - - /** - * \brief Callback called for each polygon. - * \details Routes the callback to the wrapped user action class. - * \param[in] v index of current Delaunay seed - * \param[in] P intersection between current mesh facet - * and the Voronoi cell of \p v - */ - void operator() ( - index_t v, - const GEOGen::Polygon& P - ) const { - callback_(v, RVD_.current_facet(), P); - } - - protected: - GenRestrictedVoronoiDiagram& RVD_; - GEO::RVDPolygonCallback& callback_; - }; - - - virtual void compute_with_polygon_callback( - GEO::RVDPolygonCallback& polygon_callback - ) { - create_threads(); - if(nb_parts() == 0) { - PolygonCallbackAction action(RVD_,polygon_callback); - RVD_.for_each_polygon(action); - } else { - for(index_t t = 0; t < nb_parts(); t++) { - part(t).RVD_.set_symbolic(RVD_.symbolic()); - part(t).RVD_.set_connected_components_priority( - RVD_.connected_components_priority() - ); - } - spinlocks_.resize(delaunay_->nb_vertices()); - thread_mode_ = MT_POLYG; - polygon_callback_ = &polygon_callback; - polygon_callback_->set_spinlocks(&spinlocks_); - // Note: callback begin()/end() is called in for_each_polygon() - parallel_for( - 0, nb_parts(), - parallel_for_member_callback(this, &thisclass::run_thread) - ); - polygon_callback_->set_spinlocks(nullptr); - } - } - - virtual void compute_with_polyhedron_callback( - GEO::RVDPolyhedronCallback& polyhedron_callback - ) { - create_threads(); - if(nb_parts() == 0) { - RVD_.for_each_polyhedron(polyhedron_callback); - } else { - for(index_t t = 0; t < nb_parts(); t++) { - part(t).RVD_.set_symbolic(RVD_.symbolic()); - part(t).RVD_.set_connected_components_priority( - RVD_.connected_components_priority() - ); - } - spinlocks_.resize(delaunay_->nb_vertices()); - thread_mode_ = MT_POLYH; - polyhedron_callback_ = &polyhedron_callback; - polyhedron_callback_->set_spinlocks(&spinlocks_); - // Note: callback begin()/end() is - // called in for_each_polyhedron() - parallel_for( - 0, nb_parts(), - parallel_for_member_callback(this, &thisclass::run_thread) - ); - polyhedron_callback_->set_spinlocks(nullptr); - } - } - - /********************************************************************/ - - /** - * \brief Implementation class for explicitly constructing - * a surfacic mesh that corresponds to the surfacic - * restricted Voronoi diagram. - * \details To be used as a template argument - * to RVD::for_each_polygon(). The current Vornoi cell is - * reported in facet region. - * \tparam BUILDER a class that implements iterative mesh building, - * e.g., MeshBuilder. - */ - template - class BuildRVD { - public: - /** - * \brief Constructs a new BuildRVD. - * \param[in] RVD_in the restricted Voronoi diagram - * \param[in] builder the lesh builder - */ - BuildRVD( - const GenRestrictedVoronoiDiagram& RVD_in, - BUILDER& builder - ) : - RVD(RVD_in), - builder_(builder), - current_facet_(-1) { - builder_.begin_surface(); - } - - /** - * \brief The destructor - * \details Terminates the current facet - * and the current surface. - */ - ~BuildRVD() { - if(current_facet_ != -1) { - builder_.end_reference_facet(); - } - builder_.end_surface(); - } - - /** - * \brief The callback called for each restricted Voronoi cell. - * \param[in] v index of current center vertex - * \param[in] P current restricted Voronoi cell - */ - void operator() ( - index_t v, - const typename GenRestrictedVoronoiDiagram::Polygon& P - ) { - index_t f = RVD.current_facet(); - if(signed_index_t(f) != current_facet_) { - if(current_facet_ != -1) { - builder_.end_reference_facet(); - } - current_facet_ = signed_index_t(f); - builder_.begin_reference_facet(f); - } - builder_.begin_facet(v); - for(index_t i = 0; i < P.nb_vertices(); i++) { - const Vertex& ve = P.vertex(i); - builder_.add_vertex_to_facet(ve.point(), ve.sym()); - } - builder_.end_facet(); - } - - private: - const GenRestrictedVoronoiDiagram& RVD; - BUILDER& builder_; - signed_index_t current_facet_; - }; - - /** - * \brief Implementation class for explicitly constructing - * a volumetric mesh that corresponds to the volumetric - * restricted Voronoi diagram. - * \details To be used as a template argument - * to RVD::for_each_volumetric_integration_simplex(). - * The current Voronoi cell is reported in tetrahedron region. - * \note For the moment, vertices are duplicated (will be fixed - * in a future version). - */ - class BuildVolumetricRVD { - public: - /** - * Constructs a new BuildVolumetricRVD. - * \param[in] RVD the volumetric restricted Voronoi diagram - * \param[in] dim dimension of the points (can be smaller - * than actual dimension of the RVD). - * \param[out] vertices coordinates of the generated vertices - * \param[out] triangle_vertex_indices generated triangles, as - * vertex indices triplets - * \param[out] tet_vertex_indices generated tetrahedra, as - * vertex indices 4-uples - * \param[out] triangle_regions each generated triangle has - * a region index, that corresponds to the index of the - * Voronoi cell the triangle belongs to - * \param[out] tet_regions each generated tetrahedron has - * a region index, that corresponds to the index of the - * Voronoi cell the tetrahedron belongs to - * \param[in] cell_borders_only if true, only the surfacic - * borders of the volumetric cells are saved in the mesh, else - * volumetric cells are tetrahedralized. - * \pre dim <= delaunay->dimension() - */ - BuildVolumetricRVD( - GenRestrictedVoronoiDiagram& RVD, - coord_index_t dim, - vector& vertices, - vector& triangle_vertex_indices, - vector& tet_vertex_indices, - vector& triangle_regions, - vector& tet_regions, - bool cell_borders_only - ) : - delaunay_(RVD.delaunay()), - mesh_(RVD.mesh()), - dim_(dim), - vertices_(vertices), - triangle_vertex_indices_(triangle_vertex_indices), - tet_vertex_indices_(tet_vertex_indices), - triangle_regions_(triangle_regions), - tet_regions_(tet_regions), - cell_borders_only_(cell_borders_only) - { - vertices_.clear(); - triangle_vertex_indices_.clear(); - tet_vertex_indices_.clear(); - triangle_regions_.clear(); - tet_regions_.clear(); - - // The first vertices are copied from Delaunay, - // the other ones will be created during the traversal - nb_vertices_ = delaunay_->nb_vertices(); - vertices_.resize(nb_vertices_ * dim); - for(index_t v = 0; v < delaunay_->nb_vertices(); ++v) { - for(coord_index_t c = 0; c < dim; ++c) { - vertices_[v * dim + c] = delaunay_->vertex_ptr(v)[c]; - } - } - vertex_map_.set_first_vertex_index(nb_vertices_); - } - - /** - * \brief The callback called for each integration simplex. - * \param[in] v index of current center vertex - * \param[in] v_adj (unused here) is the index of the Voronoi cell - * adjacent to t accros facet (\p v1, \p v2, \p v3) or - * -1 if it does not exists - * \param[in] t (unused here) is the index of the current - * tetrahedron - * \param[in] t_adj (unused here) is the index of the - * tetrahedron adjacent to t accros facet (\p v1, \p v2, \p v3) - * or -1 if it does not exists - * \param[in] v1 first vertex of current integration simplex - * \param[in] v2 second vertex of current integration simplex - * \param[in] v3 third vertex of current integration simplex - */ - void operator() ( - index_t v, signed_index_t v_adj, - index_t t, signed_index_t t_adj, - const Vertex& v1, const Vertex& v2, const Vertex& v3 - ) { - geo_argused(v_adj); - geo_argused(t); - - if(cell_borders_only_) { - if(signed_index_t(v) > v_adj) { - index_t iv2 = find_or_create_vertex(v, v1); - index_t iv3 = find_or_create_vertex(v, v2); - index_t iv4 = find_or_create_vertex(v, v3); - triangle_vertex_indices_.push_back(iv4); - triangle_vertex_indices_.push_back(iv3); - triangle_vertex_indices_.push_back(iv2); - triangle_regions_.push_back(signed_index_t(v)); - } - } else { - index_t iv1 = v; - index_t iv2 = find_or_create_vertex(v, v1); - index_t iv3 = find_or_create_vertex(v, v2); - index_t iv4 = find_or_create_vertex(v, v3); - - // Triangle v1,v2,v3 is on border if there is - // no adjacent seed and no adjacent tet. - if(v_adj == -1 && t_adj == -1) { - triangle_vertex_indices_.push_back(iv4); - triangle_vertex_indices_.push_back(iv3); - triangle_vertex_indices_.push_back(iv2); - triangle_regions_.push_back(signed_index_t(v)); - } - - tet_vertex_indices_.push_back(iv1); - tet_vertex_indices_.push_back(iv2); - tet_vertex_indices_.push_back(iv3); - tet_vertex_indices_.push_back(iv4); - tet_regions_.push_back(signed_index_t(v)); - } - } - - /** - * \brief The callback called for each tetrahedron - * \param[in] v index of current center vertex - * \param[in] v_adj (unused here) is the index of the Voronoi cell - * adjacent to t accros facet (\p v1, \p v2, \p v3) or - * -1 if it does not exists - * \param[in] t (unused here) is the index of the current - * tetrahedron - * \param[in] t_adj (unused here) is the index of the - * tetrahedron adjacent to t accros facet (\p v1, \p v2, \p v3) - * or -1 if it does not exists - * \param[in] v1 first vertex of current tetrahedron - * \param[in] v2 second vertex of current tetrahedron - * \param[in] v3 third vertex of current tetrahedron - * \param[in] v4 fourth vertex of current tetrahedron - */ - void operator() ( - index_t v, signed_index_t v_adj, - index_t t, signed_index_t t_adj, - const Vertex& v1, const Vertex& v2, - const Vertex& v3, const Vertex& v4 - ) { - geo_argused(v_adj); - geo_argused(t); - geo_argused(t_adj); - index_t iv1 = vertices_.size() / dim_; - for(index_t c = 0; c < dim_; ++c) { - vertices_.push_back(v1.point()[c]); - } - index_t iv2 = vertices_.size() / dim_; - for(index_t c = 0; c < dim_; ++c) { - vertices_.push_back(v2.point()[c]); - } - index_t iv3 = vertices_.size() / dim_; - for(index_t c = 0; c < dim_; ++c) { - vertices_.push_back(v3.point()[c]); - } - index_t iv4 = vertices_.size() / dim_; - for(index_t c = 0; c < dim_; ++c) { - vertices_.push_back(v4.point()[c]); - } - tet_vertex_indices_.push_back(iv1); - tet_vertex_indices_.push_back(iv2); - tet_vertex_indices_.push_back(iv3); - tet_vertex_indices_.push_back(iv4); - tet_regions_.push_back(signed_index_t(v)); - } - - protected: - /** - * \brief Retrieves the index of a vertex given its symbolic - * representation. - * \param[in] center_vertex_id index of current Voronoi seed - * \param[in] v symbolic and geometric representation of the vertex - * \return the index of the vertex - */ - index_t find_or_create_vertex( - index_t center_vertex_id, const Vertex& v - ) { - index_t result = vertex_map_.find_or_create_vertex( - center_vertex_id, v.sym() - ); - if(result >= nb_vertices_) { - geo_assert(result == nb_vertices_); - nb_vertices_ = result + 1; - for(coord_index_t c = 0; c < dim_; ++c) { - vertices_.push_back(v.point()[c]); - } - } - return result; - } - - private: - const Delaunay* delaunay_; - const Mesh* mesh_; - coord_index_t dim_; - vector& vertices_; - vector& triangle_vertex_indices_; - vector& tet_vertex_indices_; - vector& triangle_regions_; - vector& tet_regions_; - RVDVertexMap vertex_map_; - index_t nb_vertices_; - bool cell_borders_only_; - }; - - void compute_RVD( - Mesh& M, coord_index_t dim, bool cell_borders_only, - bool integration_simplices - ) override { - bool sym = RVD_.symbolic(); - RVD_.set_symbolic(true); - if(volumetric_) { - if(dim == 0) { - dim = dimension(); - } - vector vertices; - vector triangle_vertices; - vector tet_vertices; - vector triangle_regions; - vector tet_regions; - if(cell_borders_only) { - RVD_.for_each_volumetric_integration_simplex( - BuildVolumetricRVD( - RVD_, dim, - vertices, - triangle_vertices, - tet_vertices, - triangle_regions, - tet_regions, - cell_borders_only - ), - false, // Do not visit inner tetrahedra. - true // Ensure that polygonal facets are triangulated - // coherently. - ); - } else { - if(integration_simplices) { - RVD_.for_each_volumetric_integration_simplex( - BuildVolumetricRVD( - RVD_, dim, - vertices, - triangle_vertices, - tet_vertices, - triangle_regions, - tet_regions, - cell_borders_only - ), - false, // Do not visit inner tetrahedra. - true // Ensure that polygonal facets are - // triangulated coherently. - ); - } else { - RVD_.for_each_tetrahedron( - BuildVolumetricRVD( - RVD_, dim, - vertices, - triangle_vertices, - tet_vertices, - triangle_regions, - tet_regions, - cell_borders_only - ) - ); - } - } - - M.clear(true); // keep attributes - - M.vertices.assign_points(vertices,dim,true); - M.facets.assign_triangle_mesh(triangle_vertices, true); - M.cells.assign_tet_mesh(tet_vertices, true); - - // TODO: use Attribute::assign(vector, steal_args) - // when it is there... - - if(M.facets.nb() != 0) { - Attribute facet_region_attr( - M.facets.attributes(), "region" - ); - for(index_t f=0; f cell_region_attr( - M.cells.attributes(), "region" - ); - for(index_t c=0; c(RVD_, builder) - ); - } - RVD_.set_symbolic(sym); - M.show_stats("RVD"); - } - - /********************************************************************/ - - void compute_RVC( - index_t i, - Mesh& M, - Mesh& result, - bool copy_symbolic_info - ) override { - Mesh* tmp_mesh = mesh_; - mesh_ = &M; - RVD_.set_mesh(&M); - typename GenRestrictedVoronoiDiagram::Polyhedron Cell(dimension()); - Cell.initialize_from_surface_mesh(&M, RVD_.symbolic()); - RVD_.intersect_cell_cell(i, Cell); - Cell.convert_to_mesh(&result, copy_symbolic_info); - mesh_ = tmp_mesh; - RVD_.set_mesh(tmp_mesh); - } - - /********************************************************************/ - - void for_each_polygon( - GEO::RVDPolygonCallback& callback, - bool symbolic, - bool connected_comp_priority, - bool parallel - ) override { - bool sym_backup = RVD_.symbolic(); - RVD_.set_symbolic(symbolic); - RVD_.set_connected_components_priority(connected_comp_priority); - callback.begin(); - if(parallel) { - compute_with_polygon_callback(callback); - } else { - PolygonCallbackAction action(RVD_,callback); - RVD_.for_each_polygon(action); - } - callback.end(); - RVD_.set_symbolic(sym_backup); - RVD_.set_connected_components_priority(false); - } - - /********************************************************************/ - - void for_each_polyhedron( - GEO::RVDPolyhedronCallback& callback, - bool symbolic, - bool connected_comp_priority, - bool parallel - ) override { - bool sym_backup = RVD_.symbolic(); - RVD_.set_symbolic(symbolic); - RVD_.set_connected_components_priority(connected_comp_priority); - callback.set_dimension(RVD_.mesh()->vertices.dimension()); - callback.begin(); - if(parallel) { - compute_with_polyhedron_callback(callback); - } else { - RVD_.for_each_polyhedron(callback); - } - callback.end(); - RVD_.set_symbolic(sym_backup); - RVD_.set_connected_components_priority(false); - } - - /********************************************************************/ - - /** - * \brief Does the actual computation for a specific part - * in multithread mode. - * \param[in] t the index of the part. - * \pre \p t < nb_parts() - */ - void run_thread(index_t t) { - geo_assert(t < nb_parts()); - thisclass& T = part(t); - switch(thread_mode_) { - case MT_LLOYD: - { - T.compute_centroids(arg_vectors_, arg_scalars_); - } break; - case MT_NEWTON: - { - T.compute_CVT_func_grad(T.funcval_, arg_vectors_); - } break; - case MT_INT_SMPLX: - { - T.compute_integration_simplex_func_grad( - T.funcval_, arg_vectors_, simplex_func_ - ); - } break; - case MT_POLYG: - { - T.compute_with_polygon_callback( - *polygon_callback_ - ); - } break; - case MT_POLYH: - { - T.compute_with_polyhedron_callback( - *polyhedron_callback_ - ); - } break; - case MT_NONE: - geo_assert_not_reached; - } - } - - bool compute_initial_sampling_on_surface( - double* p, index_t nb_points - ) override { - geo_assert(mesh_->facets.are_simplices()); - - // We do that here, since this triggers partitioning, - // that improves data locality. Then data locality is - // inherited by the generated points. - create_threads(); - - if(facets_begin_ == -1 && facets_end_ == -1) { - Logger::out("RVD") - << "Computing initial sampling on surface, using dimension=" - << index_t(dimension_) << std::endl; - } - - return mesh_generate_random_samples_on_surface( - *mesh_, p, nb_points, vertex_weight_, facets_begin_, facets_end_ - ); - } - - bool compute_initial_sampling_in_volume( - double* p, index_t nb_points - ) override { - geo_assert(mesh_->cells.nb() != 0); - - // We do that here, since this triggers partitioning, - // that improves data locality. Then data locality is - // inherited by the generated points. - create_threads(); - - if(tets_begin_ == -1 && tets_end_ == -1) { - Logger::out("RVD") - << "Computing initial sampling in volume, using dimension=" - << index_t(dimension_) << std::endl; - } - - return mesh_generate_random_samples_in_volume( - *mesh_, p, nb_points, vertex_weight_, tets_begin_, tets_end_ - ); - } - - /** - * \brief Creates the data structures for fast projection. - * \details It decomposes the surface into triangles, and - * stores the vertices in a KdTree. - */ - void prepare_projection() { - if(!mesh_vertices_.is_null()) { - return; - } - - // Step 1: get triangles - for(index_t f = 0; f < mesh_->facets.nb(); f++) { - index_t i = mesh_->facets.corners_begin(f); - for( - index_t j = i + 1; - j + 1 < mesh_->facets.corners_end(f); j++ - ) { - triangles_.push_back(mesh_->facet_corners.vertex(i)); - triangles_.push_back(mesh_->facet_corners.vertex(j)); - triangles_.push_back(mesh_->facet_corners.vertex(j + 1)); - } - } - nb_triangles_ = index_t(triangles_.size() / 3); - - // Step 2: get vertices stars - // Step 2.1: get one-ring neighborhood - vector > stars2(mesh_->vertices.nb()); - for(index_t t = 0; t < nb_triangles_; t++) { - stars2[triangles_[3 * t]].push_back(t); - stars2[triangles_[3 * t + 1]].push_back(t); - stars2[triangles_[3 * t + 2]].push_back(t); - } - - // Step 2.2: get two-ring neighborhood - stars_.resize(mesh_->vertices.nb()); - for(index_t i = 0; i < stars2.size(); i++) { - vector Ni; - for(index_t j = 0; j < stars2[i].size(); j++) { - index_t t = stars2[i][j]; - for(index_t iv = 0; iv < 3; iv++) { - index_t v = triangles_[3 * t + iv]; - if(v != i) { - Ni.push_back(v); - } - } - } - sort_unique(Ni); - for(index_t j = 0; j < Ni.size(); j++) { - index_t k = Ni[j]; - stars_[i].insert( - stars_[i].end(), stars2[k].begin(), stars2[k].end() - ); - } - sort_unique(stars_[i]); - } - - // Step 3: create search structure - mesh_vertices_ = Delaunay::create(dimension_, "NN"); - index_t nb_vertices = mesh_->vertices.nb(); - - // TODO: BUG !! mesh_vertices_ keeps a ref. to mesh_vertices - // that is destroyed when leaving this function. - vector mesh_vertices(nb_vertices * dimension_); - for(index_t i = 0; i < nb_vertices; i++) { - for(index_t coord = 0; coord < dimension_; coord++) { - mesh_vertices[i * dimension_ + coord] = - mesh_->vertices.point_ptr(i)[coord]; - } - } - mesh_vertices_->set_vertices(nb_vertices, mesh_vertices.data()); - } - - void project_points_on_surface( - index_t nb_points, double* points, vec3* nearest, bool do_project - ) override { - - prepare_projection(); - - if(use_exact_projection_) { - for(index_t p = 0; p < nb_points; p++) { - Point P(points + p * dimension_); - double d2 = Numeric::max_float64(); - for(index_t t = 0; t < nb_triangles_; t++) { - const Point& p1 = mesh_vertex(triangles_[3 * t]); - const Point& p2 = mesh_vertex(triangles_[3 * t + 1]); - const Point& p3 = mesh_vertex(triangles_[3 * t + 2]); - - double l1, l2, l3; - Point nearestP; - - double cur_d2 = Geom::point_triangle_squared_distance( - P, p1, p2, p3, nearestP, l1, l2, l3 - ); - - if(cur_d2 < d2) { - d2 = cur_d2; - const vec3& p1_R3 = - R3_embedding(triangles_[3 * t]); - const vec3& p2_R3 = - R3_embedding(triangles_[3 * t + 1]); - const vec3& p3_R3 = - R3_embedding(triangles_[3 * t + 2]); - nearest[p] = l1 * p1_R3 + l2 * p2_R3 + l3 * p3_R3; - if(do_project) { - for(coord_index_t - coord = 0; coord < dimension_; coord++) { - (points + p * dimension_)[coord] = - nearestP[coord]; - } - } - } - } - } - return; - } - - // find nearest point on surface in star of nearest vertex - for(index_t p = 0; p < nb_points; p++) { - Point P(points + p * dimension_); - index_t v = mesh_vertices_->nearest_vertex( - points + p * dimension_ - ); - double d2 = Numeric::max_float64(); - nearest[p] = R3_embedding(v); - for(index_t i = 0; i < stars_[v].size(); i++) { - index_t t = stars_[v][i]; - const Point& p1 = mesh_vertex(triangles_[3 * t]); - const Point& p2 = mesh_vertex(triangles_[3 * t + 1]); - const Point& p3 = mesh_vertex(triangles_[3 * t + 2]); - - double l1, l2, l3; - Point nearestP; - double cur_d2 = Geom::point_triangle_squared_distance( - P, p1, p2, p3, nearestP, l1, l2, l3 - ); - if(cur_d2 < d2) { - d2 = cur_d2; - const vec3& p1_R3 = R3_embedding(triangles_[3 * t]); - const vec3& p2_R3 = R3_embedding(triangles_[3 * t + 1]); - const vec3& p3_R3 = R3_embedding(triangles_[3 * t + 2]); - nearest[p] = l1 * p1_R3 + l2 * p2_R3 + l3 * p3_R3; - if(do_project) { - for(coord_index_t coord = 0; - coord < dimension_; coord++ - ) { - (points + p * dimension_)[coord] = - nearestP[coord]; - } - } - } - } - } - } - - /********************************************************************/ - - /** - * \brief Implementation class for computing the restricted Delaunay - * triangulation. - * \details To be used as a template argument - * to RVD::for_each_primal_triangle(). - */ - class GetPrimalTriangles { - public: - /** - * \brief Creates a new GetPrimalTriangles. - * \param[out] triangles where to store the triangles - */ - GetPrimalTriangles( - vector& triangles - ) : - triangles_(triangles) { - } - - /** - * \brief The callback called for each primal triangle. - * \param[in] v1 index of the first vertex - * \param[in] v2 index of the second vertex - * \param[in] v3 index of the third vertex - */ - void operator() (index_t v1, index_t v2, index_t v3) { - triangles_.push_back(v1); - triangles_.push_back(v2); - triangles_.push_back(v3); - } - - private: - vector& triangles_; - }; - - /** - * \brief Implementation class for computing the restricted Delaunay - * triangulation of the connected components. - * - * \details The difference with GetPrimalTriangles is that when a - * restricted Voronoi cell has multiple connected components, - * more triangles are generated to account for the topology. - * To be used as a template argument to RVD::for_each_polygon(). - * The RestrictedVoronoiDiagram needs to be in connected-components - * priority mode. - */ - class GetConnectedComponentsPrimalTriangles { - public: - /** Internal representation of the polygons. */ - typedef typename GenRestrictedVoronoiDiagram::Polygon Polygon; - - /** Internal representation of the vertices. */ - typedef typename GenRestrictedVoronoiDiagram::Vertex Vertex; - - static const index_t UNINITIALIZED = index_t(-1); - static const index_t MULTI_COMP = index_t(-2); - static const index_t ON_BORDER = index_t(-3); - - /** - * \brief Constructs a new GetConnectedComponentsPrimalTriangles. - * \param[in] RVD the restricted Voronoi diagram - * \param[out] triangles where to store the triangles - * \param[out] vertices where to store the vertices - * \param[in] dimension dimension of the restricted Voronoi diagram - * \param[in] mode a combination of constants defined in RDTMode - * \param[in] seed_is_locked specifies for each seed whether it - * can be moved (to RVC centroid or projected on surface). If - * left uninitialized, all the seeds can be moved. - * \param[in] AABB an axis-aligned bounding box tree defined on - * the input surface. It is used if one of (RDT_SELECT_NEAREST, - * RDT_PROJECT_ON_SURFACE) is set in \p mode. If needed and not - * specified, then a new one is created locally. - */ - GetConnectedComponentsPrimalTriangles( - const GenRestrictedVoronoiDiagram& RVD, - vector& triangles, - vector& vertices, - coord_index_t dimension, - RDTMode mode, - const std::vector& seed_is_locked, - MeshFacetsAABB* AABB = nullptr - ) : - RVD_(RVD), - dimension_(dimension), - triangles_(triangles), - vertices_(vertices), - m_(0.0), - cur_seed_(-1), - cur_vertex_(0), - use_RVC_centroids_((mode & RDT_RVC_CENTROIDS) != 0), - select_nearest_((mode & RDT_SELECT_NEAREST) != 0), - project_on_surface_((mode & RDT_PROJECT_ON_SURFACE) != 0), - seed_is_locked_(seed_is_locked), - prefer_seeds_((mode & RDT_PREFER_SEEDS) != 0), - AABB_(AABB) - { - if(prefer_seeds_) { - seed_to_vertex_.assign( - RVD.delaunay()->nb_vertices(),index_t(UNINITIALIZED) - ); - } - } - - /** - * \brief The callback called for each restricted Voronoi cell. - * \param[in] s1 index of current center vertex - * \param[in] P current restricted Voronoi cell - */ - void operator() (index_t s1, const Polygon& P) { - if(RVD_.connected_component_changed()) { - if(cur_seed_ != -1) { - end_connected_component(); - } - begin_connected_component(s1); - } - - if(prefer_seeds_ && !component_on_border_) { - // NOTE: there is one vertex shift between - // adjacent facet and adjacent seed, there - // must be something wrong in the way the - // combinatorial information is initialized, - // to be checked !!! (should be the i index - // for both) - for(index_t i=0; i= 0 && v3 >= 0) { - triangles_.push_back(v1); - triangles_.push_back(index_t(v2)); - triangles_.push_back(index_t(v3)); - } - } - } - } - - /** - * \brief The destructor - */ - ~GetConnectedComponentsPrimalTriangles() { - - bool owns_AABB = false; - if(select_nearest_ || project_on_surface_) { - if(AABB_ == nullptr) { - // Construct an axis-aligned bounding box tree, - // do not reorder the mesh (needs to be pre-reordered) - AABB_ = new MeshFacetsAABB( - *const_cast(RVD_.mesh()),false - ); - owns_AABB = true; - } - } - - if(cur_seed_ != -1) { - end_connected_component(); - } - - if(prefer_seeds_) { - if(select_nearest_) { - for(index_t s=0; svertex_ptr(s); - - - // At this step, vertex_ptr contains - // the centroid of the connected component - // of the RVC, we now determine whether it - // should be replaced by the - // seed (or by a projection onto the surface). - - const double* vertex_ptr = &(vertices_[vbase]); - - // If the seed is nearer to the surface - // than the - // centroid of the connected component of the - // restricted Voronoi cell, then use the seed. - - double seed_dist; - vec3 seed_projection; - double vertex_dist; - vec3 vertex_projection; - - AABB_->nearest_facet( - vec3(seed_ptr), seed_projection, seed_dist - ); - AABB_->nearest_facet( - vec3(vertex_ptr), - vertex_projection, vertex_dist - ); - - if(seed_dist < vertex_dist) { - if(project_on_surface_) { - for( - coord_index_t c = 0; - c < dimension_; ++c - ) { - vertices_[vbase + c] = - seed_projection[c]; - } - } else { - for(coord_index_t c = 0; - c < dimension_; ++c - ) { - vertices_[vbase + c] = seed_ptr[c]; - } - } - } else { - if(project_on_surface_) { - for( - coord_index_t c = 0; - c < dimension_; ++c - ) { - vertices_[vbase + c] = - vertex_projection[c]; - } - } - } - } - } - } else { - - // Current mode: prefer seeds and not select nearest - // Replace all points with the seeds (provided that - // they do not correspond to multiple - // connected components). - - for(index_t s=0; svertex_ptr(s); - - for(coord_index_t c = 0; c < dimension_; ++c) { - vertices_[vbase + c] = seed_ptr[c]; - } - } - } - } - } - - if( - (!prefer_seeds_ || !select_nearest_) && project_on_surface_ - ) { - for(index_t v=0; vnearest_facet(p,q,sq_dist); - vertices_[3*v ] = q.x; - vertices_[3*v+1] = q.y; - vertices_[3*v+2] = q.z; - } - } - - if(owns_AABB) { - delete AABB_; - AABB_ = nullptr; - } - } - - protected: - /** - * \brief Tests whether a given seed is locked. - */ - bool seed_is_locked(index_t s) { - return - seed_is_locked_.size() > 0 && - seed_is_locked_[s] - ; - } - - /** - * \brief Starts a new connected component. - * \param[in] s the seed the connected component - * is associated with. - */ - void begin_connected_component(index_t s) { - cur_seed_ = signed_index_t(s); - for(coord_index_t c = 0; c < dimension_; ++c) { - vertices_.push_back(0.0); - } - m_ = 0.0; - component_on_border_ = false; - } - - /** - * \brief Terminates the current connected component. - */ - void end_connected_component() { - - if( - !use_RVC_centroids_ || - seed_is_locked(index_t(cur_seed_)) || - component_on_border_ - ) { - // Copy seed - index_t vbase = cur_vertex_ * dimension_; - const double* seed_ptr = - RVD_.delaunay()->vertex_ptr(index_t(cur_seed_)); - for(coord_index_t c = 0; c < dimension_; ++c) { - vertices_[vbase + c] = seed_ptr[c]; - } - } else { - // Use restricted Voronoi - // cell component's centroid. - double scal = (m_ < 1e-30 ? 0.0 : 1.0 / m_); - index_t vbase = cur_vertex_ * dimension_; - for(coord_index_t c = 0; c < dimension_; ++c) { - vertices_[vbase + c] *= scal; - } - } - if(prefer_seeds_) { - if(component_on_border_) { - seed_to_vertex_[index_t(cur_seed_)] = ON_BORDER; - } - switch(seed_to_vertex_[index_t(cur_seed_)]) { - case UNINITIALIZED: - seed_to_vertex_[index_t(cur_seed_)] = cur_vertex_; - break; - case ON_BORDER: - break; - default: - seed_to_vertex_[index_t(cur_seed_)] = MULTI_COMP; - break; - } - } - ++cur_vertex_; - } - - private: - const GenRestrictedVoronoiDiagram& RVD_; - coord_index_t dimension_; - vector& triangles_; - vector& vertices_; - double m_; - signed_index_t cur_seed_; - index_t cur_vertex_; - bool use_RVC_centroids_; - bool select_nearest_; - bool project_on_surface_; - const std::vector& seed_is_locked_; - bool prefer_seeds_; - vector seed_to_vertex_; - bool component_on_border_; - MeshFacetsAABB* AABB_; - }; - - /** - * \brief Implementation class for computing the restricted Delaunay - * triangulation in volume mode. - * \details To be used as a template argument - * to RVD::for_each_primal_tetrahedron(). - */ - class GetPrimalTetrahedra { - public: - /** - * \brief Creates a new GetPrimalTetrahedra. - * \param[out] tetrahedra where to store the tetrahedra - */ - GetPrimalTetrahedra( - vector& tetrahedra - ) : - tetrahedra_(tetrahedra) { - } - - /** - * \brief The callback called for each primal tetrahedron. - * \param[in] v1 index of the first vertex - * \param[in] v2 index of the second vertex - * \param[in] v3 index of the third vertex - * \param[in] v4 index of the fourth vertex - */ - void operator() (index_t v1, index_t v2, index_t v3, index_t v4) { - tetrahedra_.push_back(v1); - tetrahedra_.push_back(v2); - tetrahedra_.push_back(v3); - tetrahedra_.push_back(v4); - } - - private: - vector& tetrahedra_; - }; - - void compute_RDT( - vector& simplices, - vector& embedding, - RDTMode mode, - const vector& seed_is_locked, - MeshFacetsAABB* AABB - ) override { - if(volumetric_) { - // For the moment, only simple mode is supported - simplices.clear(); - RVD_.for_each_primal_tetrahedron( - GetPrimalTetrahedra(simplices) - ); - // Reorient the tetrahedra - index_t nb_tetrahedra = simplices.size() / 4; - for(index_t t = 0; t < nb_tetrahedra; ++t) { - const double* p1 = - delaunay()->vertex_ptr(simplices[4 * t]); - const double* p2 = - delaunay()->vertex_ptr(simplices[4 * t + 1]); - const double* - p3 = delaunay()->vertex_ptr(simplices[4 * t + 2]); - const double* - p4 = delaunay()->vertex_ptr(simplices[4 * t + 3]); - if(PCK::orient_3d(p1, p2, p3, p4) < 0) { - std::swap(simplices[4 * t], simplices[4 * t + 1]); - } - } - embedding.clear(); - embedding.reserve(dimension_ * delaunay_->nb_vertices()); - for(index_t i = 0; i < delaunay_->nb_vertices(); i++) { - for(coord_index_t coord = 0; coord < dimension_; coord++) { - embedding.push_back(delaunay_->vertex_ptr(i)[coord]); - } - } - } else { - if((mode & RDT_MULTINERVE) != 0) { - simplices.clear(); - embedding.clear(); - bool sym = RVD_.symbolic(); - RVD_.set_symbolic(true); - RVD_.set_connected_components_priority(true); - RVD_.for_each_polygon( - GetConnectedComponentsPrimalTriangles( - RVD_, simplices, embedding, RVD_.dimension(), - mode, seed_is_locked, AABB - ) - ); - RVD_.set_symbolic(sym); - RVD_.set_connected_components_priority(false); - } else { - // Simple mode: compute RDT, without any post-processing - simplices.clear(); - RVD_.for_each_primal_triangle( - GetPrimalTriangles(simplices) - ); - embedding.clear(); - embedding.reserve(dimension_ * delaunay_->nb_vertices()); - for(index_t i = 0; i < delaunay_->nb_vertices(); i++) { - for( - coord_index_t coord = 0; - coord < dimension_; ++coord - ){ - embedding.push_back( - delaunay_->vertex_ptr(i)[coord] - ); - } - } - } - } - } - - void create_threads() override { - // TODO: check if number of facets is not smaller than - // number of threads - // TODO: create parts even if facets range is specified - // (and subdivide facets range) - if(is_slave_ || facets_begin_ != -1 || facets_end_ != -1) { - return; - } - index_t nb_parts_in = Process::maximum_concurrent_threads(); - if(nb_parts() != nb_parts_in) { - if(nb_parts_in == 1) { - delete_threads(); - } else { - vector facet_ptr; - vector tet_ptr; - mesh_partition( - *mesh_, MESH_PARTITION_HILBERT, - facet_ptr, tet_ptr, nb_parts_in - ); - delete_threads(); - parts_ = new thisclass[nb_parts_in]; - nb_parts_ = nb_parts_in; - for(index_t i = 0; i < nb_parts(); ++i) { - part(i).mesh_ = mesh_; - part(i).set_delaunay(delaunay_); - part(i).R3_embedding_base_ = R3_embedding_base_; - part(i).R3_embedding_stride_ = R3_embedding_stride_; - part(i).has_weights_ = has_weights_; - part(i).master_ = this; - part(i).RVD_.set_mesh(mesh_); - part(i).set_facets_range( - facet_ptr[i], facet_ptr[i + 1] - ); - part(i).set_exact_predicates(RVD_.exact_predicates()); - part(i).set_volumetric(volumetric()); - part(i).set_check_SR(RVD_.check_SR()); - } - if(mesh_->cells.nb() != 0) { - for(index_t i = 0; i < nb_parts(); ++i) { - part(i).set_tetrahedra_range( - tet_ptr[i], tet_ptr[i + 1] - ); - } - } - geo_assert(!Process::is_running_threads()); - } - } - } - - void set_volumetric(bool x) override { - volumetric_ = x; - for(index_t i = 0; i < nb_parts(); ++i) { - part(i).set_volumetric(x); - } - } - - void set_facets_range( - index_t facets_begin, index_t facets_end - ) override { - RVD_.set_facets_range(facets_begin, facets_end); - facets_begin_ = signed_index_t(facets_begin); - facets_end_ = signed_index_t(facets_end); - } - - void set_tetrahedra_range( - index_t tets_begin, index_t tets_end - ) override { - RVD_.set_tetrahedra_range(tets_begin, tets_end); - tets_begin_ = signed_index_t(tets_begin); - tets_end_ = signed_index_t(tets_end); - } - - void delete_threads() override { - delete[] parts_; - parts_ = nullptr; - nb_parts_ = 0; - } - - /** - * \brief Gets the number of parts (or number of threads). - */ - index_t nb_parts() const { - return nb_parts_; - } - - /** - * \brief Gets a given part from its index. - * \param[in] i index of the part - * \pre \p i < nb_parts() - */ - thisclass& part(index_t i) { - geo_debug_assert(i < nb_parts()); - return parts_[i]; - } - - /** - * \copydoc RestrictedVoronoiDiagram::point_allocator() - */ - GEOGen::PointAllocator* point_allocator() override { - return RVD_.point_allocator(); - } - - - protected: - - GenRestrictedVoronoiDiagram RVD_; - - // For projection - bool use_exact_projection_; - index_t nb_triangles_; - vector triangles_; - vector > stars_; - Delaunay_var mesh_vertices_; - - // One of MT_NONE, MT_LLOYD, MT_NEWTON - ThreadMode thread_mode_; - - bool is_slave_; - - // Variables for 'master' in multithreading mode - thisclass* parts_; - index_t nb_parts_; - Process::SpinLockArray spinlocks_; - - // Newton mode with int. simplex - IntegrationSimplex* simplex_func_; - - // PolygonCallback mode. - RVDPolygonCallback* polygon_callback_; - - // PolyhedronCallback mode. - RVDPolyhedronCallback* polyhedron_callback_; - - // master stores argument for compute_centroids() and - // compute_CVT_func_grad() to pass it to the parts. - double* arg_vectors_; - double* arg_scalars_; - - // Variables for 'slaves' in multithreading mode - thisclass* master_; - double funcval_; // Newton mode: function value - - protected: - /** - * \brief Destructor - */ - ~RVD_Nd_Impl() override { - delete_threads(); - } - - private: - /** \brief Forbids construction by copy. */ - RVD_Nd_Impl(const thisclass&); - - /** \brief Forbids assignment. */ - thisclass& operator= (const thisclass&); - }; -} - -/****************************************************************************/ - -namespace GEO { - - RestrictedVoronoiDiagram* RestrictedVoronoiDiagram::create( - Delaunay* delaunay, Mesh* mesh, - const double* R3_embedding, index_t R3_embedding_stride - ) { - - geo_cite("DBLP:journals/tog/EdelsbrunnerM90"); - geo_cite("DBLP:conf/compgeom/Shewchuk96"); - geo_cite("meyer:inria-00344297"); - geo_cite("DBLP:conf/gmp/YanWLL10"); - geo_cite("DBLP:journals/cad/YanWLL13"); - geo_cite("DBLP:journals/cad/Levy16"); - - delaunay->set_stores_neighbors(true); - RestrictedVoronoiDiagram* result = nullptr; - geo_assert(delaunay != nullptr); - coord_index_t dim = delaunay->dimension(); - switch(dim) { - case 2: - result = new RVD_Nd_Impl<2>( - delaunay, mesh, R3_embedding, R3_embedding_stride - ); - break; - case 3: - result = new RVD_Nd_Impl<3>( - delaunay, mesh, R3_embedding, R3_embedding_stride - ); - break; - case 4: - result = new RVD_Nd_Impl<4>( - delaunay, mesh, R3_embedding, R3_embedding_stride - ); - break; - case 6: - result = new RVD_Nd_Impl<6>( - delaunay, mesh, R3_embedding, R3_embedding_stride - ); - break; - case 8: - result = new RVD_Nd_Impl<8>( - delaunay, mesh, R3_embedding, R3_embedding_stride - ); - break; - default: - geo_assert_not_reached; - } - if(CmdLine::get_arg("algo:predicates") == "exact") { - result->set_exact_predicates(true); - } - return result; - } - - void RestrictedVoronoiDiagram::set_delaunay(Delaunay* delaunay) { - delaunay_ = delaunay; - if(delaunay_ != nullptr) { - dimension_ = delaunay->dimension(); - } else { - dimension_ = 0; - } - } - - RestrictedVoronoiDiagram::~RestrictedVoronoiDiagram() { - } - - RestrictedVoronoiDiagram::RestrictedVoronoiDiagram( - Delaunay* delaunay, Mesh* mesh, - const double* R3_embedding, index_t R3_embedding_stride - ) : - dimension_(0), - mesh_(mesh), - R3_embedding_base_(R3_embedding), - R3_embedding_stride_(R3_embedding_stride) { - set_delaunay(delaunay); - has_weights_ = false; - facets_begin_ = -1; - facets_end_ = -1; - tets_begin_ = -1; - tets_end_ = -1; - volumetric_ = false; - } - - - void RestrictedVoronoiDiagram::compute_RDT( - Mesh& RDT, - RDTMode mode, - const vector& seed_is_locked, - MeshFacetsAABB* AABB - ) { - vector simplices; - vector embedding; - compute_RDT( - simplices, embedding, - mode, seed_is_locked, - AABB - ); - if(volumetric()) { - RDT.cells.assign_tet_mesh(dimension(),embedding,simplices,true); - } else { - RDT.facets.assign_triangle_mesh( - dimension(),embedding,simplices,true - ); - if((mode & RDT_DONT_REPAIR) == 0) { - mesh_repair(RDT); // Needed to reorient triangles - } - } - } - - -} - diff --git a/src/lib/geogram/voronoi/calc.txt b/src/lib/geogram/voronoi/calc.txt new file mode 100644 index 00000000..bdd060bb --- /dev/null +++ b/src/lib/geogram/voronoi/calc.txt @@ -0,0 +1,54 @@ + Y-a-t-il une maniere elegante d'ajouter l'intersection avec une boule +dans la classe ConvexCell ? + + * predicat: + 1) calcul x,y,z,w + 2) (x/w - cx)^2 + (y/w - cy)^2 + (z/w - cz)^2 < R2 + (x - w cx)^2 + (y - w cy)^2 + (z - w cz)^2 < w2 R2 + (x - w cx)^2 + (y - w cy)^2 + (z - w cz)^2 - (w R)^2 < 0 + Le degre devient pas mal (8 si je dis pas de betises) + Necessite un coup de PCK (c'est pas un predicat standard ?) + Normalement on devrait pouvoir re-utiliser un predicat du + diagramme de puissance, non ? + + A priori, ca devrait etre orient_3d_lifted_SOS() je pense, + a verifier.... Nope ! ca a pas l'air de pouvoir marcher, + parceque orient_3d_lifted_SOS() regarde si un coin de cellule + de Laguerre est en conflit avec un site, alors qu'ici on regarde + s'il est en conflit avec le "site continu" qui remplit tout + l'espace. + -> nouveau predicat, qui prend en argument les trois plans et + la sphere + -> le faire deja en non-exact, explicitement + -> rem: on pourrait gerer les 'rays' (triangles avec un sommet a + l'infini) explicitement, mais un peu penible, donc on peut + pre-clipper par un gros cube. + + * calcul d'un point: + x*n1x + y*n1y + z*n1z + w*n1w = 0 + x*n2x + y*n2y + z*n2z + w*n2w = 0 + (x-w cx)2/w2 + (y-w cy)2/w2 + (z-w cz)2/w2 = R2 + + Trouver un point sur le rayon + Equation parametrique du rayon + Injecter dans equation sphere + Si y'a 2 intersections, prendre celle qui est dans le polyedre + precedent (penible !) -> y-a-t-il un autre moyen de discriminer ? + + * obtenir la cellule: + la facette spherique peut avoir plusieurs composantes connexes, il faut + aller les chercher toutes (un peu penible). Mais au moins, avec vv2t, on n'a + plus le probleme du parcours du (des) bord(s) de la zone de conflit (cool !) + + * il faudra sans doute avoir un flag pour la facette spherique. + +===================================================================================== + + Calcul des aires et des volumes: est-ce qu'on peut faire des quadratures de degre 2 ? + Probablement. On decompose en triangles et tetraedres IP2 + Puis des fonctions pour calculer: + aire d'un triangle IP2 + volume et centre de masse d'un tetraedre IP2 + + (si on veut megotter, on a trois types de triangles, suivant + le nombre de cotes courbes) diff --git a/src/lib/geogram/voronoi/convex_cell.cpp b/src/lib/geogram/voronoi/convex_cell.cpp index 6d7d530b..55abe532 100755 --- a/src/lib/geogram/voronoi/convex_cell.cpp +++ b/src/lib/geogram/voronoi/convex_cell.cpp @@ -153,6 +153,57 @@ namespace VBW { geometry_dirty_ = true; } + + void ConvexCell::init_with_tet( + vec4 P0, vec4 P1, vec4 P2, vec4 P3 + ) { + clear(); + + // The vertex at infinity. + plane_eqn_[0] = make_vec4(0,0,0,0); + + // Offset for the 4 plane equations. + // Plane 0 is vertex at infinity. + index_t boff = 1; + + plane_eqn_[boff ] = P0; + plane_eqn_[boff+1] = P1; + plane_eqn_[boff+2] = P2; + plane_eqn_[boff+3] = P3; + + new_triangle(boff+3, boff+2, boff+1); + new_triangle(boff+3, boff+0, boff+2); + new_triangle(boff+3, boff+1, boff+0); + new_triangle(boff+2, boff+0, boff+1); + + // We already created 6 vertices (for the 6 bounding box + // plane equations) plus the vertex at infinity. + nb_v_ = 5; + + geometry_dirty_ = true; + } + + + void ConvexCell::init_with_tet( + vec4 P0, vec4 P1, vec4 P2, vec4 P3, + index_t P0_global_index, + index_t P1_global_index, + index_t P2_global_index, + index_t P3_global_index + ) { + geo_debug_assert(has_vglobal_); + init_with_tet(P0, P1, P2, P3); + + // Offset for the 4 plane equations. + // Plane 0 is vertex at infinity. + index_t boff = 1; + + vglobal_[boff ] = P0_global_index; + vglobal_[boff+1] = P1_global_index; + vglobal_[boff+2] = P2_global_index; + vglobal_[boff+3] = P3_global_index; + } + /***********************************************************************/ @@ -181,11 +232,20 @@ namespace VBW { index_t t = first_valid_; while(t != END_OF_LIST) { TriangleWithFlags T = get_triangle_and_flags(t); - vec4 p = compute_triangle_point(T); - p.x /= p.w; - p.y /= p.w; - p.z /= p.w; - p.w = 1.0; + vec4 p; + if(geometry_dirty_) { + p = compute_triangle_point(T); + p.x /= p.w; + p.y /= p.w; + p.z /= p.w; + p.w = 1.0; + } else { + p.x = triangle_point_[t].x; + p.y = triangle_point_[t].y; + p.z = triangle_point_[t].z; + p.w = 1.0; + } + if(shrink != 0.0) { p.x = shrink * g.x + (1.0 - shrink) * p.x; p.y = shrink * g.y + (1.0 - shrink) * p.y; @@ -719,6 +779,7 @@ namespace VBW { if(v2t_[v] != END_OF_LIST) { index_t t = v2t_[v]; + index_t count = 0; do { if(cur < 2) { t1t2[cur] = ushort(t); @@ -733,6 +794,8 @@ namespace VBW { ++cur; index_t lv = triangle_find_vertex(t,v); t = triangle_adjacent(t, (lv + 1)%3); + ++count; + geo_assert(count < 100000); } while(t != v2t_[v]); } @@ -777,6 +840,8 @@ namespace VBW { ushort t1t2[2]; index_t cur=0; index_t t = v2t_[v]; + + index_t count = 0; do { if(cur < 2) { t1t2[cur] = ushort(t); @@ -792,15 +857,29 @@ namespace VBW { ++cur; index_t lv = triangle_find_vertex(t,v); t = triangle_adjacent(t, (lv + 1)%3); + ++count; + geo_assert(count < 100000); } while(t != v2t_[v]); } return result; } vec3 ConvexCell::barycenter() const { + vec3 result; + double m; + compute_mg(m, result); + if(m != 0.0) { + result.x /= m; + result.y /= m; + result.z /= m; + } + return result; + } + + void ConvexCell::compute_mg(double& m, vec3& result) const { vbw_assert(!geometry_dirty_); - vec3 result = make_vec3(0.0, 0.0, 0.0); - double m = 0.0; + result = make_vec3(0.0, 0.0, 0.0); + m = 0.0; ushort t_origin = END_OF_LIST; for(index_t v=0; v::max(); for(index_t v=0; v* facet_attr=nullptr ) const; + #endif /** @@ -683,6 +714,15 @@ namespace VBW { */ vec3 barycenter() const; + /** + * \brief Computes volume and barycenter. + * \param[out] m the computed volume + * \param[out] mg the computed volume times the barycenter + * \details compute_geometry() needs to be called before. + */ + void compute_mg(double& m, vec3& mg) const ; + + /** * \brief Computes the squared radius of the smallest sphere * containing the cell and centered on a point. @@ -1145,6 +1185,42 @@ namespace VBW { void grow_v(); + /** + * \brief Swaps two ConvexCells. + * \param[in] other the ConvexCell to be + * exchanged with this ConvexCell. + */ + void swap(ConvexCell& other) { + std::swap(max_t_,other.max_t_); + std::swap(max_v_,other.max_v_); + std::swap(t_,other.t_); + std::swap(vv2t_,other.vv2t_); + std::swap(plane_eqn_,other.plane_eqn_); + std::swap(nb_t_,other.nb_t_); + std::swap(nb_v_,other.nb_v_); + std::swap(first_free_,other.first_free_); + std::swap(first_valid_,other.first_valid_); + std::swap(geometry_dirty_,other.geometry_dirty_); + std::swap(triangle_point_,other.triangle_point_); + std::swap(v2t_,other.v2t_); + std::swap(vglobal_,other.vglobal_); + std::swap(has_vglobal_,other.has_vglobal_); + std::swap(tflags_,other.tflags_); + std::swap(has_tflags_,other.has_tflags_); +#ifndef STANDALONE_CONVEX_CELL + std::swap(use_exact_predicates_,other.use_exact_predicates_); +#endif + } + + /** + * \brief Gets a modifiable reference to a triangle point. + * \param[in] t the index + * \return a modifiable reference to the stored point + */ + vec3& stored_triangle_point(ushort t) { + return triangle_point_[t]; + } + protected: /** @@ -1159,7 +1235,8 @@ namespace VBW { plane_eqn_[v] = P; geometry_dirty_ = true; } - + + private: /** \brief number of allocated triangles */ @@ -1245,4 +1322,10 @@ namespace GEO { using VBW::ConvexCell; } +namespace std { + template<> inline void swap(VBW::ConvexCell& c1, VBW::ConvexCell& c2) { + c1.swap(c2); + } +} + #endif diff --git a/src/lib/geogram_gfx/GLUP/GLUP.cpp b/src/lib/geogram_gfx/GLUP/GLUP.cpp index 50538481..dcba4f7d 100644 --- a/src/lib/geogram_gfx/GLUP/GLUP.cpp +++ b/src/lib/geogram_gfx/GLUP/GLUP.cpp @@ -45,7 +45,6 @@ #include #include -#include #include #include #include @@ -77,15 +76,7 @@ namespace { << CmdLine::get_config_file_name() << "\' in " << std::endl; Logger::out("GLUP") << " your home directory (" << FileSystem::home_directory() << ")" << std::endl; Logger::out("GLUP") << " with: " << std::endl; - Logger::out("GLUP") << " gfx:GL_profile=core" << std::endl; - Logger::out("GLUP") << " (3) create a file named \'" - << CmdLine::get_config_file_name() << "\' in " << std::endl; - Logger::out("GLUP") << " your home directory (" << FileSystem::home_directory() << ")" << std::endl; - Logger::out("GLUP") << " with: " << std::endl; - Logger::out("GLUP") << " gfx:GL_profile=compatibility" << std::endl; - Logger::out("GLUP") << " gfx:GLUP_profile=VanillaGL" << std::endl; - Logger::out("GLUP") << " Note: solution (3) will result in degraded performance." - << std::endl; + Logger::out("GLUP") << " gfx:GLUP_profile=GLUPES2" << std::endl; } } } @@ -399,41 +390,18 @@ GLUPcontext glupCreateContext() { result->setup(); } catch(...) { GEO::Logger::warn("GLUP") - << "Caught an exception in GLUPES2, downgrading to VanillaGL" + << "Caught an exception in GLUPES2" << std::endl; downgrade_message(); - GLUP_profile = "VanillaGL"; delete result; result = nullptr; } } #endif -#ifdef GEO_GL_LEGACY - if(GLUP_profile == "VanillaGL") { - if(GEO::CmdLine::get_arg("gfx:GL_profile") != "compatibility") { - GEO::Logger::warn("GLUP") - << "Cannot switch to VanillaGL" << std::endl; - GEO::Logger::warn("GLUP") - << "Needs gfx:GL_profile=compatibility" << std::endl; - } else { - try { - result = new GLUP::Context_VanillaGL; - result->setup(); - } catch(...) { - GEO::Logger::warn("GLUP") - << "Caught an exception in VanillaGL" - << std::endl; - delete result; - result = nullptr; - } - } - } -#endif - if(result == nullptr) { - GEO::Logger::err("GLUP") << "Could not create context" - << std::endl; + GEO::Logger::err("GLUP") << "Could not create context" + << std::endl; } else { GLUP::all_contexts_.insert(result); } @@ -473,18 +441,6 @@ void glupMakeCurrent(GLUPcontext context) { GLUP::current_context_ = reinterpret_cast(context); } -void glupCopyFromGLState(GLUPbitfield which_attributes) { - GEO_CHECK_GL(); - GLUP::current_context_->copy_from_GL_state(which_attributes); - GEO_CHECK_GL(); -} - -void glupCopyToGLState(GLUPbitfield which_attributes) { - GEO_CHECK_GL(); - GLUP::current_context_->copy_to_GL_state(which_attributes); - GEO_CHECK_GL(); -} - GLUPboolean glupPrimitiveSupportsArrayMode(GLUPprimitive prim) { GEO_CHECK_GL(); return GLUP::current_context_->primitive_supports_array_mode(prim) ? diff --git a/src/lib/geogram_gfx/GLUP/GLUP.h b/src/lib/geogram_gfx/GLUP/GLUP.h index 339afd0d..50ff48d8 100644 --- a/src/lib/geogram_gfx/GLUP/GLUP.h +++ b/src/lib/geogram_gfx/GLUP/GLUP.h @@ -172,27 +172,6 @@ extern "C" { GLUP_ALL_ATTRIBUTES = 15 } GLUPattributes; - /** - * \brief Copies some variable from the current fixed - * functionality pipeline of OpenGL to the current - * GLUP context. - * \param[in] which_attributes an '|'-combination of - * GLUP_MATRICES_ATTRIBUTES_BIT, GLUP_COLORS_ATTRIBUTES_BIT, - * GLUP_LIGHTING_ATTRIBUTES_BIT, GLUP_CLIPPING_ATTRIBUTES_BIT or - * GLUP_ALL_ATTRIBUTES. - */ - void GLUP_API glupCopyFromGLState(GLUPbitfield which_attributes); - - /** - * \brief Copies some variables from the current GLUP context - * to the fixed functionality pipeline of OpenGL. - * \param[in] which_attributes an '|'-combination of - * GLUP_MATRICES_ATTRIBUTES_BIT, GLUP_COLORS_ATTRIBUTES_BIT, - * GLUP_LIGHTING_ATTRIBUTES_BIT, GLUP_CLIPPING_ATTRIBUTES_BIT or - * GLUP_ALL_ATTRIBUTES. - */ - void GLUP_API glupCopyToGLState(GLUPbitfield which_attributes); - /** * \brief Binds the GLUP uniform state to a program. * \param[in] program the handle to the GLSL program. diff --git a/src/lib/geogram_gfx/GLUP/GLUP_context.cpp b/src/lib/geogram_gfx/GLUP/GLUP_context.cpp index 3ce54647..c0e956b1 100644 --- a/src/lib/geogram_gfx/GLUP/GLUP_context.cpp +++ b/src/lib/geogram_gfx/GLUP/GLUP_context.cpp @@ -874,168 +874,6 @@ namespace GLUP { ); return uniform_buffer_data_ + offset; } - - void Context::copy_from_GL_state(GLUPbitfield which_attributes) { -#ifndef GEO_GL_LEGACY - geo_argused(which_attributes); - return; -#else - // No "GL state" in core profile. - if(use_core_profile_) { - return; - } - - uniform_buffer_dirty_ = true; - - // I observed that without these instructions, - // glGet() does not always - // return the latest set values under Windows. - glFlush(); - glFinish(); - - if(which_attributes & GLUP_MATRICES_ATTRIBUTES_BIT) { - matrices_dirty_ = true; - glGetFloatv( - GL_PROJECTION_MATRIX, - matrix_stack_[GLUP_PROJECTION_MATRIX].top() - ); - glGetFloatv( - GL_MODELVIEW_MATRIX, - matrix_stack_[GLUP_MODELVIEW_MATRIX].top() - ); - glGetFloatv( - GL_TEXTURE_MATRIX, - matrix_stack_[GLUP_TEXTURE_MATRIX].top() - ); - } - - if(which_attributes & GLUP_CLIPPING_ATTRIBUTES_BIT) { - uniform_state_.toggle[GLUP_CLIPPING].set( - glIsEnabled(GL_CLIP_PLANE0) - ); - GLdouble clip_plane_d[4]; - glGetClipPlane(GL_CLIP_PLANE0, clip_plane_d); - copy_vector( - uniform_state_.clip_plane.get_pointer(), clip_plane_d, 4 - ); - - mult_matrix_vector( - uniform_state_.world_clip_plane.get_pointer(), - uniform_state_.modelview_matrix.get_pointer(), - uniform_state_.clip_plane.get_pointer() - ); - - GLfloat projection_invert[16]; - GLboolean OK = invert_matrix( - projection_invert, - uniform_state_.projection_matrix.get_pointer() - ); - if(!OK) { - Logger::warn("GLUP") << "Singular projection matrix" - << std::endl; - show_matrix(uniform_state_.projection_matrix.get_pointer()); - } - mult_matrix_vector( - uniform_state_.clip_clip_plane.get_pointer(), - projection_invert, - uniform_state_.clip_plane.get_pointer() - ); - - } - - if(which_attributes & GLUP_LIGHTING_ATTRIBUTES_BIT) { - lighting_dirty_ = true; - uniform_state_.toggle[GLUP_LIGHTING].set(glIsEnabled(GL_LIGHTING)); - GLfloat light[4]; - glGetLightfv(GL_LIGHT0, GL_POSITION, light); - copy_vector(uniform_state_.light_vector.get_pointer(), light, 3); - } - - if(which_attributes & GLUP_COLORS_ATTRIBUTES_BIT) { - glGetMaterialfv( - GL_FRONT, GL_DIFFUSE, - uniform_state_.color[GLUP_FRONT_COLOR].get_pointer() - ); - glGetMaterialfv( - GL_BACK, GL_DIFFUSE, - uniform_state_.color[GLUP_BACK_COLOR].get_pointer() - ); - } -#endif - } - - void Context::copy_to_GL_state(GLUPbitfield which_attributes) { -#ifndef GEO_GL_LEGACY - geo_argused(which_attributes); - return; -#else - // No "GL state" in core profile. - if(use_core_profile_) { - return; - } - - GLint matrix_mode_save; - glGetIntegerv(GL_MATRIX_MODE, &matrix_mode_save); - - if(which_attributes & GLUP_MATRICES_ATTRIBUTES_BIT) { - glMatrixMode(GL_PROJECTION); - glLoadMatrixf(matrix_stack_[GLUP_PROJECTION_MATRIX].top()); - glMatrixMode(GL_MODELVIEW); - glLoadMatrixf(matrix_stack_[GLUP_MODELVIEW_MATRIX].top()); - glMatrixMode(GL_TEXTURE); - glLoadMatrixf(matrix_stack_[GLUP_TEXTURE_MATRIX].top()); - } - - if(which_attributes & GLUP_CLIPPING_ATTRIBUTES_BIT) { - if(uniform_state_.toggle[GLUP_CLIPPING].get()) { - glEnable(GL_CLIP_PLANE0); - } else { - glDisable(GL_CLIP_PLANE0); - } - GLdouble clip_plane_d[4]; - glGetClipPlane(GL_CLIP_PLANE0, clip_plane_d); - copy_vector( - clip_plane_d, uniform_state_.clip_plane.get_pointer(), 4 - ); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - glClipPlane(GL_CLIP_PLANE0, clip_plane_d); - glPopMatrix(); - } - - if(which_attributes & GLUP_LIGHTING_ATTRIBUTES_BIT) { - if(uniform_state_.toggle[GLUP_LIGHTING].get()) { - glEnable(GL_LIGHTING); - glEnable(GL_LIGHT0); - } else { - glDisable(GL_LIGHTING); - glDisable(GL_LIGHT0); - } - GLfloat light[4]; - copy_vector(light, uniform_state_.light_vector.get_pointer(), 3); - light[3] = 0.0f; - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - glLightfv(GL_LIGHT0, GL_POSITION, light); - glPopMatrix(); - } - - if(which_attributes & GLUP_COLORS_ATTRIBUTES_BIT) { - glMaterialfv( - GL_FRONT, GL_DIFFUSE, - uniform_state_.color[GLUP_FRONT_COLOR].get_pointer() - ); - glMaterialfv( - GL_BACK, GL_DIFFUSE, - uniform_state_.color[GLUP_BACK_COLOR].get_pointer() - ); - } - - glMatrixMode(GLenum(matrix_mode_save)); -#endif - } void Context::bind_uniform_state(GLuint program) { #ifndef GEO_GL_150 diff --git a/src/lib/geogram_gfx/GLUP/GLUP_context.h b/src/lib/geogram_gfx/GLUP/GLUP_context.h index 4596789f..f66f737d 100644 --- a/src/lib/geogram_gfx/GLUP/GLUP_context.h +++ b/src/lib/geogram_gfx/GLUP/GLUP_context.h @@ -1056,28 +1056,6 @@ namespace GLUP { * functionalities are not implemented in the OpenGL driver. */ virtual void setup(); - - - /** - * \brief Copies the relevant variables from OpenGL - * fixed functionality pipeline to this GLUP context. - * \param[in] which_attributes an '|'-combination of - * GLUP_MATRICES_ATTRIBUTES_BIT, GLUP_COLORS_ATTRIBUTES_BIT, - * GLUP_LIGHTING_ATTRIBUTES_BIT, GLUP_CLIPPING_ATTRIBUTES_BIT or - * GLUP_ALL_ATTRIBUTES. - */ - virtual void copy_from_GL_state(GLUPbitfield which_attributes); - - - /** - * \brief Copies the relevant variables from this GLUP - * context to OpenGL fixed functionality pipeline. - * \param[in] which_attributes an '|'-combination of - * GLUP_MATRICES_ATTRIBUTES_BIT, GLUP_COLORS_ATTRIBUTES_BIT, - * GLUP_LIGHTING_ATTRIBUTES_BIT, GLUP_CLIPPING_ATTRIBUTES_BIT or - * GLUP_ALL_ATTRIBUTES. - */ - virtual void copy_to_GL_state(GLUPbitfield which_attributes); /** * \brief Binds GLUP uniform state to a program. diff --git a/src/lib/geogram_gfx/GLUP/GLUP_context_VanillaGL.cpp b/src/lib/geogram_gfx/GLUP/GLUP_context_VanillaGL.cpp deleted file mode 100755 index 3d90683f..00000000 --- a/src/lib/geogram_gfx/GLUP/GLUP_context_VanillaGL.cpp +++ /dev/null @@ -1,938 +0,0 @@ -/* - * Copyright (c) 2012-2016, Bruno Levy - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * * Neither the name of the ALICE Project-Team nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * If you modify this software, you should include a notice giving the - * name of the person performing the modification, the date of modification, - * and the reason for such modification. - * - * Contact: Bruno Levy - * - * Bruno.Levy@inria.fr - * http://www.loria.fr/~levy - * - * ALICE Project - * LORIA, INRIA Lorraine, - * Campus Scientifique, BP 239 - * 54506 VANDOEUVRE LES NANCY CEDEX - * FRANCE - * - */ - -#include -#include -#include -#include -#include -#include - -#ifdef GEO_GL_LEGACY - -namespace GLUP { - using namespace GEO; - - /***********************************************************************/ - - GLuint create_sphere_display_list(); - - Context_VanillaGL::Context_VanillaGL() { - use_core_profile_ = false; - use_ES_profile_ = false; - indirect_texturing_program_ = 0; - sphere_display_list_ = create_sphere_display_list(); - } - - Context_VanillaGL::~Context_VanillaGL() { - if(indirect_texturing_program_ != 0) { - glDeleteProgramsARB(1, &indirect_texturing_program_); - indirect_texturing_program_ = 0; - } - if(sphere_display_list_ != 0) { - glDeleteLists(sphere_display_list_, 1); - sphere_display_list_ = 0; - } - } - - const char* Context_VanillaGL::profile_name() const { - return "VanillaGL"; - } - - void Context_VanillaGL::begin(GLUPprimitive primitive) { - - if(!primitive_info_[primitive].implemented) { - Logger::warn("GLUP") - << "glupBegin(): " - << glup_primitive_name(primitive) - << " not implemented in this profile" << std::endl; - } - - update_uniform_buffer(); - - if(uniform_state_.toggle[GLUP_VERTEX_COLORS].get()) { - immediate_state_.buffer[GLUP_COLOR_ATTRIBUTE].enable(); - } else { - immediate_state_.buffer[GLUP_COLOR_ATTRIBUTE].disable(); - } - - if(uniform_state_.toggle[GLUP_TEXTURING].get()) { - immediate_state_.buffer[GLUP_TEX_COORD_ATTRIBUTE].enable(); - } else { - immediate_state_.buffer[GLUP_TEX_COORD_ATTRIBUTE].disable(); - } - - if( - uniform_state_.toggle[GLUP_LIGHTING].get() && - uniform_state_.toggle[GLUP_VERTEX_NORMALS].get() - ) { - immediate_state_.buffer[GLUP_NORMAL_ATTRIBUTE].enable(); - } else { - immediate_state_.buffer[GLUP_NORMAL_ATTRIBUTE].disable(); - } - - immediate_state_.begin(primitive); - prepare_to_draw(primitive); - configure_OpenGL_texturing(); - configure_OpenGL_lighting(); - configure_OpenGL_clipping(); - configure_OpenGL_picking(); - } - - void Context_VanillaGL::prepare_to_draw(GLUPprimitive primitive) { - if(primitive == GLUP_POINTS) { - glPointSize(uniform_state_.point_size.get()); - } - } - - void Context_VanillaGL::do_update_uniform_buffer() { - copy_to_GL_state(GLUP_CLIPPING_ATTRIBUTES_BIT); - copy_to_GL_state(GLUP_COLORS_ATTRIBUTES_BIT); - Context::do_update_uniform_buffer(); - } - - void Context_VanillaGL::update_matrices() { - if(matrices_dirty_) { - Context::update_matrices(); - copy_to_GL_state(GLUP_MATRICES_ATTRIBUTES_BIT); - } - } - - void Context_VanillaGL::update_lighting() { - if(lighting_dirty_) { - Context::update_matrices(); - copy_to_GL_state(GLUP_LIGHTING_ATTRIBUTES_BIT); - } - } - - void Context_VanillaGL::configure_OpenGL_texturing() { - glDisable(GLUP_TEXTURE_1D_TARGET); - glDisable(GLUP_TEXTURE_2D_TARGET); - glDisable(GLUP_TEXTURE_3D_TARGET); - if( !uniform_state_.toggle[GLUP_PICKING].get() && - uniform_state_.toggle[GLUP_TEXTURING].get() - ) { - if(uniform_state_.toggle[GLUP_INDIRECT_TEXTURING].get()) { - begin_indirect_texturing(); - } else { - switch(uniform_state_.texture_type.get()) { - case GLUP_TEXTURE_1D: - glEnable(GLUP_TEXTURE_1D_TARGET); - break; - case GLUP_TEXTURE_2D: { - // Copy the 2D texture bound to unit 1 to unit 0 - // else the fixed functionality pipeline cannot - // see it. - glActiveTexture(GL_TEXTURE0 + GLUP_TEXTURE_2D_UNIT); - GLint tex; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &tex); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GLUP_TEXTURE_2D_TARGET, GLuint(tex)); - glEnable(GLUP_TEXTURE_2D_TARGET); - } break; - case GLUP_TEXTURE_3D: { -#ifdef GEO_GL_TEXTURE_3D - // Copy the 3D texture bound to unit 2 to unit 0 - // else the fixed functionality pipeline cannot - // see it. - glActiveTexture(GL_TEXTURE0 + GLUP_TEXTURE_3D_UNIT); - GLint tex; - glGetIntegerv(GL_TEXTURE_BINDING_3D, &tex); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GLUP_TEXTURE_3D_TARGET, GLuint(tex)); - glEnable(GLUP_TEXTURE_3D_TARGET); -#endif - } break; - } - } - - glMatrixMode(GL_TEXTURE); - glLoadMatrixf(get_matrix(GLUP_TEXTURE_MATRIX)); - glMatrixMode(GL_MODELVIEW); - - switch(uniform_state_.texture_mode.get()) { - case GLUP_TEXTURE_REPLACE: - // Yes, it's GL_MODULATE also for texture replace, - // because GL_TEXTURE_REPLACE removes the shading ! - if(uniform_state_.toggle[GLUP_LIGHTING].get()) { - glTexEnvi( - GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE - ); - } else { - glTexEnvi( - GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE - ); - } - break; - case GLUP_TEXTURE_MODULATE: - glTexEnvi( - GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE - ); - break; - case GLUP_TEXTURE_ADD: - glTexEnvi( - GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD - ); - break; - } - } - } - - void Context_VanillaGL::configure_OpenGL_lighting() { - GLUPprimitive primitive = immediate_state_.primitive(); - - if(primitive == GLUP_POINTS || primitive == GLUP_LINES) { - glDisable(GL_LIGHTING); - } else { - if(uniform_state_.toggle[GLUP_LIGHTING].get()) { - glEnable(GL_LIGHTING); - glEnable(GL_NORMALIZE); - } else { - glDisable(GL_LIGHTING); - } - } - - if( - uniform_state_.toggle[GLUP_VERTEX_COLORS].get() || - primitive == GLUP_POINTS || - primitive == GLUP_LINES - ) { - glEnable(GL_COLOR_MATERIAL); - } else { - glEnable(GL_COLOR_MATERIAL); - glColor3f(1.0f, 1.0f, 1.0f); - glDisable(GL_COLOR_MATERIAL); - } - - - glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE); - glColor4fv(uniform_state_.color[GLUP_FRONT_COLOR].get_pointer()); - - glMaterialfv( - GL_FRONT, GL_DIFFUSE, - uniform_state_.color[GLUP_FRONT_COLOR].get_pointer() - ); - glMaterialfv( - GL_BACK, GL_DIFFUSE, - uniform_state_.color[GLUP_BACK_COLOR].get_pointer() - ); - - static GLfloat ambient[4] = { 0.2f, 0.2f, 0.2f, 1.0f }; - static GLfloat zero[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; - - glMaterialfv( - GL_FRONT_AND_BACK, GL_AMBIENT, ambient - ); - if(uniform_state_.specular.get() != 0.0f) { - GLfloat specular[4]; - specular[0] = uniform_state_.specular.get(); - specular[1] = uniform_state_.specular.get(); - specular[2] = uniform_state_.specular.get(); - specular[3] = uniform_state_.specular.get(); - glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular); - glMaterialf( - GL_FRONT_AND_BACK, GL_SHININESS, 30.0 - ); - } else { - glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, zero); - glMaterialf( - GL_FRONT_AND_BACK, GL_SHININESS, 0.0 - ); - } - glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR); - } - - void Context_VanillaGL::configure_OpenGL_picking() { - if(uniform_state_.toggle[GLUP_PICKING].get()) { - - glEnable(GL_COLOR_MATERIAL); - glDisable(GL_LIGHTING); - - // Disable colors and texture coordinates - immediate_state_.buffer[GLUP_COLOR_ATTRIBUTE].disable(); - immediate_state_.buffer[GLUP_TEX_COORD_ATTRIBUTE].disable(); - - // Disable buffers for interpolated clipping - // attributes. - glDisableClientState(GL_COLOR_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - - uniform_state_.base_picking_id.set(0); - - switch(uniform_state_.picking_mode.get()) { - case GLUP_PICK_PRIMITIVE: { - pick_primitives_ = true; - } break; - case GLUP_PICK_CONSTANT: { - pick_primitives_ = false; - glPickingIdAsColor(index_t(uniform_state_.picking_id.get())); - } break; - } - } else { - pick_primitives_ = false; - } - } - - void Context_VanillaGL::configure_OpenGL_clipping() { - if(clip_slice_cells()) { - glNormal3f( - -world_clip_plane_[0], - -world_clip_plane_[1], - -world_clip_plane_[2] - ); - glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE); - - // Bind the intersection buffers as vertex array, colors and texture - // coordinates (if enabled for the last two ones). - glEnableClientState(GL_VERTEX_ARRAY); - - glVertexPointer( - 4, - GL_FLOAT, - 0, // stride - &(isect_vertex_attribute_[0][0]) - ); - - if(immediate_state_.buffer[1].is_enabled()) { - glEnableClientState(GL_COLOR_ARRAY); - glColorPointer( - 4, - GL_FLOAT, - 0, // stride - &(isect_vertex_attribute_[1][0]) - ); - } - - if(immediate_state_.buffer[2].is_enabled()) { - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glTexCoordPointer( - 4, - GL_FLOAT, - 0, // stride - &(isect_vertex_attribute_[2][0]) - ); - } - } - } - - void Context_VanillaGL::end() { - flush_immediate_buffers(); - if(clip_slice_cells()) { - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - } - if(uniform_state_.toggle[GLUP_INDIRECT_TEXTURING].get()) { - end_indirect_texturing(); - } - } - - void Context_VanillaGL::begin_indirect_texturing() { - - // TODO: there is one texture matrix per texture unit, - // make sure that we do what we meant to do (associate - // the correct matrix with the correct unit) - // Here, texture matrix is associated with unit 2, whereas - // it acts on the colormap (needs to be fixed...) - - // TODO: indirect texturing is only implemented for 3D textures - // for now. Implement it for 2D textures also if needed. - - // Note: indirect texturing uses a fragment shader, not very - // "Vanilla", but not very "spicy" either (just an ARBfp1.0 - // shader, that most graphic boards should support without - // any problem, it has been there since OpenGL 2.0). - - // This program reads the 3D texture (bound to unit 2), - // transforms the read value with the texture matrix, - // and then reads the colormap (bound to unit 0) using - // the transformed value. - - static const char* fshader_source = - "!!ARBfp1.0 " - "TEMP R0; " - "TEX R0, fragment.texcoord[0], texture[2], 3D; " - "DP4 R0.x, state.matrix.texture[2].row[0], R0; " - "TEX result.color, R0, texture[0], 2D; " - "END " - ; - - if(indirect_texturing_program_ == 0) { - glGenProgramsARB(1, &indirect_texturing_program_) ; - glEnable(GL_FRAGMENT_PROGRAM_ARB); - glBindProgramARB( - GL_FRAGMENT_PROGRAM_ARB, indirect_texturing_program_ - ); - glProgramStringARB( - GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, - GLsizei(strlen(fshader_source)), fshader_source - ); - GLint errpos ; - glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &errpos) ; - bool ok = ( errpos == -1) ; - if(!ok) { - const char* s = (const char*)( - glGetString(GL_PROGRAM_ERROR_STRING_ARB) - ); - Logger::err("ARBfp") - << ":" << errpos << ": " << s << std::endl ; - } - } - glBindProgramARB( - GL_FRAGMENT_PROGRAM_ARB, indirect_texturing_program_ - ); - glEnable(GL_FRAGMENT_PROGRAM_ARB); - } - - void Context_VanillaGL::end_indirect_texturing() { - glDisable(GL_FRAGMENT_PROGRAM_ARB); - } - - void Context_VanillaGL::setup() { - create_CPU_side_uniform_buffer(); - } - - - - void Context_VanillaGL::flush_immediate_buffers() { - shrink_cells_in_immediate_buffers(); - classify_vertices_in_immediate_buffers(); - - glPushAttrib( - GL_ENABLE_BIT | GL_LIGHTING_BIT | - GL_POLYGON_BIT | GL_TEXTURE_BIT | - GL_CURRENT_BIT - ); - - if( - uniform_state_.clipping_mode.get() != GLUP_CLIP_STANDARD && - immediate_state_.primitive() != GLUP_POINTS && - immediate_state_.primitive() != GLUP_LINES && - immediate_state_.primitive() != GLUP_TRIANGLES && - immediate_state_.primitive() != GLUP_QUADS - ) { - glDisable(GL_CLIP_PLANE0); - } - - flush_immediate_buffers_once(); - - if(uniform_state_.toggle[GLUP_PICKING].get()) { - if(pick_primitives_) { - uniform_state_.base_picking_id.set( - uniform_state_.base_picking_id.get() + - int(immediate_state_.nb_primitives()) - ); - } - } else if( - uniform_state_.toggle[GLUP_DRAW_MESH].get() && - immediate_state_.primitive() != GLUP_POINTS && - immediate_state_.primitive() != GLUP_LINES && - immediate_state_.primitive() != GLUP_SPHERES - ) { - // Do it one more time for the mesh - - glDisable(GL_LIGHTING); - glDisable(GLUP_TEXTURE_1D_TARGET); - glDisable(GLUP_TEXTURE_2D_TARGET); - glDisable(GLUP_TEXTURE_3D_TARGET); - glDisable(GL_COLOR_MATERIAL); - - // Disable vertex attributes. - bool va1_enabled = immediate_state_.buffer[1].is_enabled(); - bool va2_enabled = immediate_state_.buffer[2].is_enabled(); - immediate_state_.buffer[1].disable(); - immediate_state_.buffer[2].disable(); - - if(clip_slice_cells()) { - glDisableClientState(GL_COLOR_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - } - - glColor3fv(uniform_state_.color[GLUP_MESH_COLOR].get_pointer()); - glLineWidth(uniform_state_.mesh_width.get()); - glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); - - flush_immediate_buffers_once(); - - // Restore previous state for vertex attributes. - if(va1_enabled) { - immediate_state_.buffer[1].enable(); - if(clip_slice_cells()) { - glEnableClientState(GL_COLOR_ARRAY); - } - } - if(va2_enabled) { - immediate_state_.buffer[2].enable(); - if(clip_slice_cells()) { - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - } - } - } - - glColor3f(1.0f, 1.0f, 1.0f); - glPopAttrib(); - - immediate_state_.reset(); - } - - void Context_VanillaGL::flush_immediate_buffers_once() { - switch(immediate_state_.primitive()) { - case GLUP_POINTS: - draw_immediate_buffer_GLUP_POINTS(); - break; - case GLUP_LINES: - draw_immediate_buffer_GLUP_LINES(); - break; - case GLUP_TRIANGLES: - draw_immediate_buffer_GLUP_TRIANGLES(); - break; - case GLUP_QUADS: - draw_immediate_buffer_GLUP_QUADS(); - break; - case GLUP_TETRAHEDRA: - draw_immediate_buffer_GLUP_TETRAHEDRA(); - break; - case GLUP_HEXAHEDRA: - draw_immediate_buffer_GLUP_HEXAHEDRA(); - break; - case GLUP_PRISMS: - draw_immediate_buffer_GLUP_PRISMS(); - break; - case GLUP_PYRAMIDS: - draw_immediate_buffer_GLUP_PYRAMIDS(); - break; - case GLUP_CONNECTORS: - draw_immediate_buffer_GLUP_CONNECTORS(); - break; - case GLUP_SPHERES: - draw_immediate_buffer_GLUP_SPHERES(); - break; - case GLUP_NB_PRIMITIVES: - geo_assert_not_reached; - } - } - - void Context_VanillaGL::setup_immediate_buffers() { - } - - void Context_VanillaGL::setup_primitives() { - primitive_info_.resize(GLUP_NB_PRIMITIVES); - primitive_info_[GLUP_POINTS].implemented = true; - primitive_info_[GLUP_LINES].implemented = true; - primitive_info_[GLUP_TRIANGLES].implemented = true; - primitive_info_[GLUP_QUADS].implemented = true; - primitive_info_[GLUP_TETRAHEDRA].implemented = true; - primitive_info_[GLUP_HEXAHEDRA].implemented = true; - primitive_info_[GLUP_PYRAMIDS].implemented = true; - primitive_info_[GLUP_PRISMS].implemented = true; - primitive_info_[GLUP_CONNECTORS].implemented = true; - primitive_info_[GLUP_SPHERES].implemented = true; - } - - Memory::pointer Context_VanillaGL::get_state_variable_address( - const char* name - ) { - geo_assert(variable_to_offset_.find(name) != variable_to_offset_.end()); - return uniform_buffer_data_ + variable_to_offset_[name]; - } - - bool Context_VanillaGL::primitive_supports_array_mode( - GLUPprimitive prim - ) const { - geo_argused(prim); - return false; - } - - void Context_VanillaGL::draw_immediate_buffer_GLUP_POINTS() { - glDisable(GL_LIGHTING); - glEnable(GL_POINT_SMOOTH); - glBegin(GL_POINTS); - for(index_t v=0; v -#include -#include - -/** - * \file geogram_gfx/GLUP/GLUP_context_VanillaGL.h - * \brief Internal implementation of GLUP using plain old OpenGL. - */ - -#ifdef GEO_GL_LEGACY - -namespace GLUP { - using namespace GEO; - - /*********************************************************************/ - - /** - * \brief Implementation of GLUP using Vanilla (old-style) OpenGL. - * \details This implementation does not use any shader. It is used - * as a fallback when the initialization of the other ones fails. - * Some primitive may be not implemented, degraded or of very low - * performance. - */ - class Context_VanillaGL : public Context { - public: - /** - * \brief Context_VanillaGL constructor. - */ - Context_VanillaGL(); - - /** - * \brief Context_VanillaGL destructor. - */ - virtual ~Context_VanillaGL(); - - /** - * \copydoc Context::profile_name() - */ - virtual const char* profile_name() const; - - /** - * \copydoc Context::primitive_supports_array_mode() - */ - virtual bool primitive_supports_array_mode(GLUPprimitive prim) const; - - /** - * \copydoc Context::begin() - */ - virtual void begin(GLUPprimitive primitive); - - /** - * \copydoc Context::end() - */ - virtual void end(); - - protected: - - /** - * \copydoc Context::prepare_to_draw() - */ - virtual void prepare_to_draw(GLUPprimitive primitive); - - /** - * \copydoc Context::do_update_uniform_buffer() - */ - virtual void do_update_uniform_buffer(); - - /** - * \copydoc Context::update_matrices() - */ - virtual void update_matrices(); - - /** - * \copydoc Context::update_lighting() - */ - virtual void update_lighting(); - - /** - * \brief Configures texturing-related OpenGL state - * variables according to the GLUP state variables. - * \details This function is called by begin() - */ - void configure_OpenGL_texturing(); - - /** - * \brief Configures lighting-related OpenGL state - * variables according to the GLUP state variables. - * \details This function is called by begin() - */ - void configure_OpenGL_lighting(); - - /** - * \brief Configures clipping-related OpenGL state - * variables according to the GLUP state variables. - * \details This function is called by begin(). - */ - void configure_OpenGL_clipping(); - - /** - * \brief Configures lighting-related OpenGL state - * variables according to the GLUP state variables. - * \details This function is called by begin(). It needs - * to be called after configure_OpenGL_texturing() and - * configure_OpenGL_lighting() since it overrides texturing and - * lighting settings. - */ - void configure_OpenGL_picking(); - - - /** - * \copydoc Context::setup() - */ - virtual void setup(); - - /** - * \copydoc Context::flush_immediate_buffers() - */ - virtual void flush_immediate_buffers(); - - - /** - * \brief Flushes the immediate buffer with the - * current drawing modes. - * \details This function is separated from - * flush_immediate_buffers(), since we need to - * flush the buffer twice when mesh drawing is - * enabled. - */ - virtual void flush_immediate_buffers_once(); - - /** - * \copydoc Context::setup_immediate_buffers() - */ - virtual void setup_immediate_buffers(); - - - /** - * \copydoc Context::setup_primitives() - */ - virtual void setup_primitives(); - - /** - * \copydoc Context::get_state_variable_address() - */ - Memory::pointer get_state_variable_address(const char* name); - - /** - * \brief Tests whether cells should be sliced. - * \retval true if cells should be sliced - * \retval false otherwise - */ - bool clip_slice_cells() const { - return ( - uniform_state_.toggle[GLUP_CLIPPING].get() && - uniform_state_.clipping_mode.get() == GLUP_CLIP_SLICE_CELLS && - immediate_state_.primitive() >= GLUP_TETRAHEDRA - ) ; - } - - /** - * \brief Sends a vertex and its optional attributes to OpenGL. - * \param[in] v the index of the vertex from the immediate buffer. - */ - void output_vertex(index_t v) { - if(immediate_state_.buffer[GLUP_COLOR_ATTRIBUTE].is_enabled()) { - glColor4fv( - immediate_state_.buffer[GLUP_COLOR_ATTRIBUTE].element_ptr(v) - ); - } - if(immediate_state_.buffer[GLUP_TEX_COORD_ATTRIBUTE].is_enabled()) { - glTexCoord4fv( - immediate_state_.buffer[GLUP_TEX_COORD_ATTRIBUTE]. - element_ptr(v) - ); - } - if(immediate_state_.buffer[GLUP_NORMAL_ATTRIBUTE].is_enabled()) { - glNormal3fv( - immediate_state_.buffer[GLUP_NORMAL_ATTRIBUTE]. - element_ptr(v) - ); - } - glVertex4fv( - immediate_state_.buffer[GLUP_VERTEX_ATTRIBUTE].element_ptr(v) - ); - } - - /** - * \brief Sends a triangle normal to OpenGL - * \param[in] v1 , v2 , v3 the indices of the three vertices from - * the immediate buffer. - */ - void output_normal(index_t v1, index_t v2, index_t v3); - - /** - * \brief Sends a quad normal to OpenGL - * \param[in] v1 , v2 , v3 , v4 the indices of the four vertices from - * the immediate buffer. - */ - void output_normal(index_t v1, index_t v2, index_t v3, index_t v4); - - - /** - * \brief Sends a picking id to OpenGL and encodes it as a color. - * \details The current base picking id is added to the id. - * If picking is deactivated or constant by object, - * it does nothing. - */ - void output_picking_id(index_t id) { - if(pick_primitives_) { - glPickingIdAsColor( - index_t(uniform_state_.base_picking_id.get()) + id - ); - } - } - - /** - * \brief Encodes a 32 bits integer into the - * current OpenGL color. - * \details This function is used by the - * picking mechanism. - * \param[in] id the picking id to be encoded as - * the current OpenGL color - */ - static void glPickingIdAsColor(index_t id) { - GLubyte r = GLubyte( id & 255); - GLubyte g = GLubyte((id >> 8) & 255); - GLubyte b = GLubyte((id >> 16) & 255); - GLubyte a = GLubyte((id >> 24) & 255); - glColor4ub(r,g,b,a); - } - - /** - * \brief Sends a flat-shaded triangle to OpenGL - * \param[in] v1 , v2 , v3 the indices of the three vertices from the - * immediate buffer. - */ - void draw_triangle(index_t v1, index_t v2, index_t v3) { - if( - uniform_state_.toggle[GLUP_LIGHTING].get() && - !uniform_state_.toggle[GLUP_VERTEX_NORMALS].get() - ) { - output_normal(v1,v2,v3); - } - output_vertex(v1); - output_vertex(v2); - output_vertex(v3); - } - - /** - * \brief Sends a flat-shaded quad to OpenGL - * \param[in] v1 , v2 , v3 , v4 the indices of the three - * vertices from the immediate buffer. - */ - void draw_quad(index_t v1, index_t v2, index_t v3, index_t v4) { - if(uniform_state_.toggle[GLUP_LIGHTING].get() && - !uniform_state_.toggle[GLUP_VERTEX_NORMALS].get() - ) { - output_normal(v1,v2,v3,v4); - } - output_vertex(v1); - output_vertex(v2); - output_vertex(v4); - output_vertex(v3); - } - - /** - * \brief Sends the contents of the immediate buffers to - * OpenGL, as point primitives. - */ - void draw_immediate_buffer_GLUP_POINTS(); - - /** - * \brief Sends the contents of the immediate buffers to - * OpenGL, as line primitives. - */ - void draw_immediate_buffer_GLUP_LINES(); - - /** - * \brief Sends the contents of the immediate buffers to - * OpenGL, as triangle primitives. - */ - void draw_immediate_buffer_GLUP_TRIANGLES(); - - /** - * \brief Sends the contents of the immediate buffers to - * OpenGL, as quad primitives. - */ - void draw_immediate_buffer_GLUP_QUADS(); - - /** - * \brief Sends the contents of the immediate buffers to - * OpenGL, as tetrahedra primitives. - */ - void draw_immediate_buffer_GLUP_TETRAHEDRA(); - - /** - * \brief Sends the contents of the immediate buffers to - * OpenGL, as hexahedra primitives. - */ - void draw_immediate_buffer_GLUP_HEXAHEDRA(); - - /** - * \brief Sends the contents of the immediate buffers to - * OpenGL, as prism primitives. - */ - void draw_immediate_buffer_GLUP_PRISMS(); - - /** - * \brief Sends the contents of the immediate buffers to - * OpenGL, as pyramid primitives. - */ - void draw_immediate_buffer_GLUP_PYRAMIDS(); - - /** - * \brief Sends the contents of the immediate buffers to - * OpenGL, as connectors primitives. - */ - void draw_immediate_buffer_GLUP_CONNECTORS(); - - /** - * \brief Sends the contents of the immediate buffers to - * OpenGL, as spheres primitives. - */ - void draw_immediate_buffer_GLUP_SPHERES(); - - /** - * \brief Draws all the primitives from the immediate buffer using - * the marching cells algorithm. - * \details This function is used when clipping is enabled and when - * clippping mode is GLUP_CLIP_SLICE_CELLS - */ - void draw_immediate_buffer_with_marching_cells( - const MarchingCell& cell - ); - - void begin_indirect_texturing(); - - void end_indirect_texturing(); - - private: - /** - * \brief Indicates whether a picking id should be send to - * OpenGL for each primitive. - */ - bool pick_primitives_; - - /** - * \brief The program to be used for indirect - * texturing. - */ - GLuint indirect_texturing_program_; - - /** - * \brief A display list that draws a tesselated - * unit sphere. - */ - GLuint sphere_display_list_; - }; - - /*********************************************************************/ - - -} - -#endif - -#endif diff --git a/src/lib/geogram_gfx/GLUP/GLUP_sphere.cpp b/src/lib/geogram_gfx/GLUP/GLUP_sphere.cpp deleted file mode 100755 index 5453062d..00000000 --- a/src/lib/geogram_gfx/GLUP/GLUP_sphere.cpp +++ /dev/null @@ -1,2028 +0,0 @@ -/* - * Copyright (c) 2012-2016, Bruno Levy - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * * Neither the name of the ALICE Project-Team nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * If you modify this software, you should include a notice giving the - * name of the person performing the modification, the date of modification, - * and the reason for such modification. - * - * Contact: Bruno Levy - * - * Bruno.Levy@inria.fr - * http://www.loria.fr/~levy - * - * ALICE Project - * LORIA, INRIA Lorraine, - * Campus Scientifique, BP 239 - * 54506 VANDOEUVRE LES NANCY CEDEX - * FRANCE - * - */ - -#include -#include -#include - - -namespace GLUP { - - GLuint create_sphere_display_list(); - - -#ifdef GEO_GL_LEGACY - GLuint create_sphere_display_list() { - - const unsigned int SPHERE_MODEL_NB_PTS = 642; - const unsigned int SPHERE_MODEL_NB_TRIANGLES = 1280; - - static float SPHERE_MODEL_PTS[SPHERE_MODEL_NB_PTS*3] = { - 0.0f, 0.0f, 1.0f, - 0.894427f, 0.0f, 0.447214f, - 0.276393f, 0.850651f, 0.447214f, - -0.723607f, 0.525731f, 0.447214f, - -0.723607f, -0.525731f, 0.447214f, - 0.276393f, -0.850651f, 0.447214f, - 0.723607f, 0.525731f, -0.447214f, - 0.723607f, -0.525731f, -0.447214f, - -0.276393f, 0.850651f, -0.447214f, - -0.894427f, 0.0f, -0.447214f, - -0.276393f, -0.850651f, -0.447214f, - 0.0f, 0.0f, -1.0f, - 0.525731f, 0.0f, 0.850651f, - 0.688191f, 0.5f, 0.525731f, - 0.16246f, 0.5f, 0.850651f, - -0.262866f, 0.809017f, 0.525731f, - -0.425325f, 0.309017f, 0.850651f, - -0.850651f, 0.0f, 0.525731f, - -0.425325f, -0.309017f, 0.850651f, - -0.262866f, -0.809017f, 0.525731f, - 0.16246f, -0.5f, 0.850651f, - 0.688191f, -0.5f, 0.525731f, - 0.587785f, -0.809017f, -2.61678e-010f, - 0.951057f, -0.309017f, 4.48188e-008f, - 0.850651f, 0.0f, -0.525731f, - 0.951057f, 0.309017f, 4.48188e-008f, - 0.587785f, 0.809017f, -2.61678e-010f, - 0.262866f, 0.809017f, -0.525731f, - 0.0f, 1.0f, 0.0f, - -0.587785f, 0.809017f, 2.61678e-010f, - -0.688191f, 0.5f, -0.525731f, - -0.951057f, 0.309017f, -4.48188e-008f, - -0.951057f, -0.309017f, -4.48188e-008f, - -0.688191f, -0.5f, -0.525731f, - -0.587785f, -0.809017f, 2.61678e-010f, - 0.0f, -1.0f, 0.0f, - 0.262866f, -0.809017f, -0.525731f, - 0.425325f, -0.309017f, -0.850651f, - 0.425325f, 0.309017f, -0.850651f, - -0.16246f, 0.5f, -0.850651f, - -0.16246f, -0.5f, -0.850651f, - -0.525731f, 0.0f, -0.850651f, - 0.273267f, 0.0f, 0.961938f, - 0.822619f, 0.259892f, 0.505721f, - 0.228109f, 0.702046f, 0.674609f, - 0.00703146f, 0.862668f, 0.505721f, - -0.597196f, 0.433889f, 0.674609f, - -0.818274f, 0.273267f, 0.505721f, - -0.597196f, -0.433889f, 0.674609f, - -0.512752f, -0.69378f, 0.505721f, - 0.228109f, -0.702046f, 0.674609f, - 0.501375f, -0.702046f, 0.505721f, - 0.449186f, -0.862668f, 0.232454f, - 0.870463f, -0.433889f, -0.232454f, - 0.818274f, -0.273267f, -0.505721f, - 0.870463f, 0.433889f, -0.232454f, - 0.68164f, 0.69378f, -0.232454f, - 0.512752f, 0.69378f, -0.505721f, - -0.143665f, 0.961938f, -0.232454f, - -0.449186f, 0.862668f, -0.232454f, - -0.501375f, 0.702046f, -0.505721f, - -0.959253f, 0.160622f, -0.232454f, - -0.959253f, -0.160622f, -0.232454f, - -0.822619f, -0.259892f, -0.505721f, - -0.449186f, -0.862668f, -0.232454f, - -0.143665f, -0.961938f, -0.232454f, - -0.00703146f, -0.862668f, -0.505721f, - 0.597196f, -0.433889f, -0.674609f, - 0.221077f, 0.160622f, -0.961938f, - -0.084444f, 0.259892f, -0.961938f, - -0.228109f, -0.702046f, -0.674609f, - -0.273267f, 0.0f, -0.961938f, - 0.738175f, 0.0f, 0.674609f, - 0.501375f, 0.702046f, 0.505721f, - 0.084444f, 0.259892f, 0.961938f, - -0.512752f, 0.69378f, 0.505721f, - -0.221077f, 0.160622f, 0.961938f, - -0.818274f, -0.273267f, 0.505721f, - -0.221077f, -0.160622f, 0.961938f, - 0.00703146f, -0.862668f, 0.505721f, - 0.084444f, -0.259892f, 0.961938f, - 0.822619f, -0.259892f, 0.505721f, - 0.68164f, -0.69378f, -0.232454f, - 0.959253f, -0.160622f, 0.232454f, - 0.818274f, 0.273267f, -0.505721f, - 0.959253f, 0.160622f, 0.232454f, - 0.449186f, 0.862668f, 0.232454f, - -0.00703146f, 0.862668f, -0.505721f, - 0.143665f, 0.961938f, 0.232454f, - -0.68164f, 0.69378f, 0.232454f, - -0.822619f, 0.259892f, -0.505721f, - -0.870463f, 0.433889f, 0.232454f, - -0.870463f, -0.433889f, 0.232454f, - -0.501375f, -0.702046f, -0.505721f, - -0.68164f, -0.69378f, 0.232454f, - 0.143665f, -0.961938f, 0.232454f, - 0.512752f, -0.69378f, -0.505721f, - 0.221077f, -0.160622f, -0.961938f, - 0.597196f, 0.433889f, -0.674609f, - -0.228109f, 0.702046f, -0.674609f, - -0.084444f, -0.259892f, -0.961938f, - -0.738175f, 0.0f, -0.674609f, - 0.447214f, 0.525731f, 0.723607f, - 0.361803f, 0.262866f, 0.894427f, - 0.638197f, 0.262866f, 0.723607f, - -0.361803f, 0.587785f, 0.723607f, - -0.138197f, 0.425325f, 0.894427f, - -0.0527864f, 0.688191f, 0.723607f, - -0.67082f, -0.16246f, 0.723607f, - -0.447214f, 0.0f, 0.894427f, - -0.67082f, 0.16246f, 0.723607f, - -0.0527864f, -0.688191f, 0.723607f, - -0.138197f, -0.425325f, 0.894427f, - -0.361803f, -0.587785f, 0.723607f, - 0.638197f, -0.262866f, 0.723607f, - 0.361803f, -0.262866f, 0.894427f, - 0.447214f, -0.525731f, 0.723607f, - 0.809017f, -0.587785f, 2.34251e-008f, - 0.861803f, -0.425325f, 0.276393f, - 0.67082f, -0.688191f, 0.276393f, - 0.947214f, 0.16246f, -0.276393f, - 1.0f, 0.0f, 4.71253e-008f, - 0.947214f, -0.16246f, -0.276393f, - 0.67082f, 0.688191f, 0.276393f, - 0.861803f, 0.425325f, 0.276393f, - 0.809017f, 0.587785f, 2.34251e-008f, - 0.138197f, 0.951057f, -0.276393f, - 0.309017f, 0.951057f, -1.37573e-010f, - 0.447214f, 0.850651f, -0.276393f, - -0.447214f, 0.850651f, 0.276393f, - -0.138197f, 0.951057f, 0.276393f, - -0.309017f, 0.951057f, 1.37573e-010f, - -0.861803f, 0.425325f, -0.276393f, - -0.809017f, 0.587785f, -2.34251e-008f, - -0.67082f, 0.688191f, -0.276393f, - -0.947214f, -0.16246f, 0.276393f, - -0.947214f, 0.16246f, 0.276393f, - -1.0f, 0.0f, -4.71253e-008f, - -0.67082f, -0.688191f, -0.276393f, - -0.809017f, -0.587785f, -2.34251e-008f, - -0.861803f, -0.425325f, -0.276393f, - -0.138197f, -0.951057f, 0.276393f, - -0.447214f, -0.850651f, 0.276393f, - -0.309017f, -0.951057f, 1.37573e-010f, - 0.447214f, -0.850651f, -0.276393f, - 0.309017f, -0.951057f, -1.37573e-010f, - 0.138197f, -0.951057f, -0.276393f, - 0.447214f,0.0f, -0.894427f, - 0.67082f, 0.16246f, -0.723607f, - 0.67082f, -0.16246f, -0.723607f, - 0.0527864f, 0.688191f, -0.723607f, - 0.361803f, 0.587785f, -0.723607f, - 0.138197f, 0.425325f, -0.894427f, - 0.138197f, -0.425325f, -0.894427f, - 0.361803f, -0.587785f, -0.723607f, - 0.0527864f, -0.688191f, -0.723607f, - -0.638197f, 0.262866f, -0.723607f, - -0.447214f, 0.525731f, -0.723607f, - -0.361803f, 0.262866f, -0.894427f, - -0.447214f, -0.525731f, -0.723607f, - -0.638197f, -0.262866f, -0.723607f, - -0.361803f, -0.262866f, -0.894427f, - 0.137952f,0.0f, 0.990439f, - 0.866811f, 0.1312f, 0.481067f, - 0.254686f, 0.783843f, 0.566326f, - 0.14308f, 0.864929f, 0.481067f, - -0.666777f, 0.484442f, 0.566326f, - -0.778382f, 0.403355f, 0.481067f, - -0.666777f, -0.484442f, 0.566326f, - -0.624147f, -0.615642f, 0.481067f, - 0.254686f, -0.783843f, 0.566326f, - 0.392638f, -0.783843f, 0.481067f, - 0.366292f, -0.864929f, 0.343115f, - 0.804729f, -0.484442f, -0.343115f, - 0.778382f, -0.403355f, -0.481067f, - 0.804729f, 0.484442f, -0.343115f, - 0.709406f, 0.615642f, -0.343115f, - 0.624147f, 0.615642f, -0.481067f, - -0.212056f, 0.915043f, -0.343115f, - -0.366292f, 0.864929f, -0.343115f, - -0.392638f, 0.783843f, -0.481067f, - -0.935787f, 0.0810863f, -0.343115f, - -0.935787f, -0.0810863f, -0.343115f, - -0.866811f, -0.1312f, -0.481067f, - -0.366292f, -0.864929f, -0.343115f, - -0.212056f, -0.915043f, -0.343115f, - -0.14308f, -0.864929f, -0.481067f, - 0.666777f, -0.484442f, -0.566326f, - 0.111606f, 0.0810863f, -0.990439f, - -0.0426296f, 0.1312f, -0.990439f, - -0.254686f, -0.783843f, -0.566326f, - -0.137952f,0.0f, -0.990439f, - 0.638054f,0.0f, 0.769992f, - 0.600525f, 0.606825f, 0.520705f, - 0.124644f, 0.383614f, 0.915043f, - -0.391553f, 0.758652f, 0.520705f, - -0.326321f, 0.237086f, 0.915043f, - -0.842518f, -0.137952f, 0.520705f, - -0.326321f, -0.237086f, 0.915043f, - -0.129152f, -0.843911f, 0.520705f, - 0.124644f, -0.383614f, 0.915043f, - 0.762697f, -0.383614f, 0.520705f, - 0.64084f, -0.758652f, -0.117349f, - 0.964375f, -0.237086f, 0.117349f, - 0.842518f, 0.137952f, -0.520705f, - 0.964375f, 0.237086f, 0.117349f, - 0.523491f, 0.843911f, 0.117349f, - 0.129152f, 0.843911f, -0.520705f, - 0.0725258f, 0.990439f, 0.117349f, - -0.64084f, 0.758652f, 0.117349f, - -0.762697f, 0.383614f, -0.520705f, - -0.919552f, 0.375039f, 0.117349f, - -0.919552f, -0.375039f, 0.117349f, - -0.600525f, -0.606825f, -0.520705f, - -0.64084f, -0.758652f, 0.117349f, - 0.0725258f, -0.990439f, 0.117349f, - 0.391553f, -0.758652f, -0.520705f, - 0.326321f, -0.237086f, -0.915043f, - 0.516196f, 0.375039f, -0.769992f, - -0.197169f, 0.606825f, -0.769992f, - -0.124644f, -0.383614f, -0.915043f, - -0.638054f,0.0f, -0.769992f, - 0.308637f, 0.519258f, 0.79694f, - 0.449299f, 0.133071f, 0.883415f, - 0.671461f, 0.386187f, 0.632456f, - -0.39847f, 0.453991f, 0.79694f, - 0.0122828f, 0.46843f, 0.883415f, - -0.159793f, 0.757935f, 0.632456f, - -0.554905f, -0.238677f, 0.79694f, - -0.441708f, 0.156434f, 0.883415f, - -0.770218f, 0.0822425f, 0.632456f, - 0.0555203f, -0.601501f, 0.79694f, - -0.285273f, -0.371748f, 0.883415f, - -0.316228f, -0.707107f, 0.632456f, - 0.589218f, -0.133071f, 0.79694f, - 0.265399f, -0.386187f, 0.883415f, - 0.574779f, -0.519258f, 0.632456f, - 0.891007f, -0.453991f, 3.45473e-008f, - 0.784658f, -0.46843f, 0.406061f, - 0.637147f, -0.757935f, 0.139919f, - 0.960966f, 0.238677f, -0.139919f, - 0.987688f, -0.156434f, 4.65451e-008f, - 0.910138f, -0.0822425f, -0.406061f, - 0.687976f, 0.601501f, 0.406061f, - 0.917729f, 0.371748f, 0.139919f, - 0.707107f, 0.707107f, 1.17261e-008f, - 0.0699596f, 0.987688f, -0.139919f, - 0.45399f, 0.891007f, -2.02114e-010f, - 0.359465f, 0.840178f, -0.406061f, - -0.359465f, 0.840178f, 0.406061f, - -0.0699596f, 0.987688f, 0.139919f, - -0.45399f, 0.891007f, 2.02114e-010f, - -0.917729f, 0.371748f, -0.139919f, - -0.707107f, 0.707107f, -1.17261e-008f, - -0.687976f, 0.601501f, -0.406061f, - -0.910138f, -0.0822425f, 0.406061f, - -0.960966f, 0.238677f, 0.139919f, - -0.987688f, -0.156434f, -4.65451e-008f, - -0.637147f, -0.757935f, -0.139919f, - -0.891007f, -0.453991f, -3.45473e-008f, - -0.784658f, -0.46843f, -0.406061f, - -0.203031f, -0.891007f, 0.406061f, - -0.52395f, -0.840178f, 0.139919f, - -0.156434f, -0.987688f, 6.96437e-011f, - 0.52395f, -0.840178f, -0.139919f, - 0.156434f, -0.987688f, -6.96437e-011f, - 0.203031f, -0.891007f, -0.406061f, - 0.441708f, 0.156434f, -0.883415f, - 0.770218f, 0.0822425f, -0.632456f, - 0.554905f, -0.238677f, -0.79694f, - 0.159793f, 0.757935f, -0.632456f, - 0.39847f, 0.453991f, -0.79694f, - -0.0122828f, 0.46843f, -0.883415f, - 0.285273f, -0.371748f, -0.883415f, - 0.316228f, -0.707107f, -0.632456f, - -0.0555203f, -0.601501f, -0.79694f, - -0.671461f, 0.386187f, -0.632456f, - -0.308637f, 0.519258f, -0.79694f, - -0.449299f, 0.133071f, -0.883415f, - -0.574779f, -0.519258f, -0.632456f, - -0.589218f, -0.133071f, -0.79694f, - -0.265399f, -0.386187f, -0.883415f, - 0.403355f,0.0f, 0.915043f, - 0.762697f, 0.383614f, 0.520705f, - 0.197169f, 0.606825f, 0.769992f, - -0.129152f, 0.843911f, 0.520705f, - -0.516196f, 0.375039f, 0.769992f, - -0.842518f, 0.137952f, 0.520705f, - -0.516196f, -0.375039f, 0.769992f, - -0.391553f, -0.758652f, 0.520705f, - 0.197169f, -0.606825f, 0.769992f, - 0.600525f, -0.606825f, 0.520705f, - 0.523491f, -0.843911f, 0.117349f, - 0.919552f, -0.375039f, -0.117349f, - 0.842518f, -0.137952f, -0.520705f, - 0.919552f, 0.375039f, -0.117349f, - 0.64084f, 0.758652f, -0.117349f, - 0.391553f, 0.758652f, -0.520705f, - -0.0725258f, 0.990439f, -0.117349f, - -0.523491f, 0.843911f, -0.117349f, - -0.600525f, 0.606825f, -0.520705f, - -0.964375f, 0.237086f, -0.117349f, - -0.964375f, -0.237086f, -0.117349f, - -0.762697f, -0.383614f, -0.520705f, - -0.523491f, -0.843911f, -0.117349f, - -0.0725258f, -0.990439f, -0.117349f, - 0.129152f, -0.843911f, -0.520705f, - 0.516196f, -0.375039f, -0.769992f, - 0.326321f, 0.237086f, -0.915043f, - -0.124644f, 0.383614f, -0.915043f, - -0.197169f, -0.606825f, -0.769992f, - -0.403355f,0.0f, -0.915043f, - 0.824181f,0.0f, 0.566326f, - 0.392638f, 0.783843f, 0.481067f, - 0.0426296f, 0.1312f, 0.990439f, - -0.624147f, 0.615642f, 0.481067f, - -0.111606f, 0.0810863f, 0.990439f, - -0.778382f, -0.403355f, 0.481067f, - -0.111606f, -0.0810863f, 0.990439f, - 0.14308f, -0.864929f, 0.481067f, - 0.0426296f, -0.1312f, 0.990439f, - 0.866811f, -0.1312f, 0.481067f, - 0.709406f, -0.615642f, -0.343115f, - 0.935787f, -0.0810863f, 0.343115f, - 0.778382f, 0.403355f, -0.481067f, - 0.935787f, 0.0810863f, 0.343115f, - 0.366292f, 0.864929f, 0.343115f, - -0.14308f, 0.864929f, -0.481067f, - 0.212056f, 0.915043f, 0.343115f, - -0.709406f, 0.615642f, 0.343115f, - -0.866811f, 0.1312f, -0.481067f, - -0.804729f, 0.484442f, 0.343115f, - -0.804729f, -0.484442f, 0.343115f, - -0.392638f, -0.783843f, -0.481067f, - -0.709406f, -0.615642f, 0.343115f, - 0.212056f, -0.915043f, 0.343115f, - 0.624147f, -0.615642f, -0.481067f, - 0.111606f, -0.0810863f, -0.990439f, - 0.666777f, 0.484442f, -0.566326f, - -0.254686f, 0.783843f, -0.566326f, - -0.0426296f, -0.1312f, -0.990439f, - -0.824181f,0.0f, -0.566326f, - 0.574779f, 0.519258f, 0.632456f, - 0.265399f, 0.386187f, 0.883415f, - 0.589218f, 0.133071f, 0.79694f, - -0.316228f, 0.707107f, 0.632456f, - -0.285273f, 0.371748f, 0.883415f, - 0.0555203f, 0.601501f, 0.79694f, - -0.770218f, -0.0822425f, 0.632456f, - -0.441708f, -0.156434f, 0.883415f, - -0.554905f, 0.238677f, 0.79694f, - -0.159793f, -0.757935f, 0.632456f, - 0.0122828f, -0.46843f, 0.883415f, - -0.39847f, -0.453991f, 0.79694f, - 0.671461f, -0.386187f, 0.632456f, - 0.449299f, -0.133071f, 0.883415f, - 0.308637f, -0.519258f, 0.79694f, - 0.707107f, -0.707107f, 1.17261e-008f, - 0.917729f, -0.371748f, 0.139919f, - 0.687976f, -0.601501f, 0.406061f, - 0.910138f, 0.0822425f, -0.406061f, - 0.987688f, 0.156434f, 4.65451e-008f, - 0.960966f, -0.238677f, -0.139919f, - 0.637147f, 0.757935f, 0.139919f, - 0.784658f, 0.46843f, 0.406061f, - 0.891007f, 0.453991f, 3.45473e-008f, - 0.203031f, 0.891007f, -0.406061f, - 0.156434f, 0.987688f, -6.96437e-011f, - 0.52395f, 0.840178f, -0.139919f, - -0.52395f, 0.840178f, 0.139919f, - -0.203031f, 0.891007f, 0.406061f, - -0.156434f, 0.987688f, 6.96437e-011f, - -0.784658f, 0.46843f, -0.406061f, - -0.891007f, 0.453991f, -3.45473e-008f, - -0.637147f, 0.757935f, -0.139919f, - -0.960966f, -0.238677f, 0.139919f, - -0.910138f, 0.0822425f, 0.406061f, - -0.987688f, 0.156434f, -4.65451e-008f, - -0.687976f, -0.601501f, -0.406061f, - -0.707107f, -0.707107f, -1.17261e-008f, - -0.917729f, -0.371748f, -0.139919f, - -0.0699596f, -0.987688f, 0.139919f, - -0.359465f, -0.840178f, 0.406061f, - -0.45399f, -0.891007f, 2.02114e-010f, - 0.359465f, -0.840178f, -0.406061f, - 0.45399f, -0.891007f, -2.02114e-010f, - 0.0699596f, -0.987688f, -0.139919f, - 0.441708f, -0.156434f, -0.883415f, - 0.554905f, 0.238677f, -0.79694f, - 0.770218f, -0.0822425f, -0.632456f, - -0.0555203f, 0.601501f, -0.79694f, - 0.316228f, 0.707107f, -0.632456f, - 0.285273f, 0.371748f, -0.883415f, - -0.0122828f, -0.46843f, -0.883415f, - 0.39847f, -0.453991f, -0.79694f, - 0.159793f, -0.757935f, -0.632456f, - -0.589218f, 0.133071f, -0.79694f, - -0.574779f, 0.519258f, -0.632456f, - -0.265399f, 0.386187f, -0.883415f, - -0.308637f, -0.519258f, -0.79694f, - -0.671461f, -0.386187f, -0.632456f, - -0.449299f, -0.133071f, -0.883415f, - 0.409955f, 0.399607f, 0.819909f, - 0.506732f, 0.266405f, 0.819909f, - 0.550012f, 0.399607f, 0.733349f, - -0.253366f, 0.513375f, 0.819909f, - -0.0967772f, 0.564254f, 0.819909f, - -0.210086f, 0.646578f, 0.733349f, - -0.566543f, -0.0823236f, 0.819909f, - -0.566543f, 0.0823236f, 0.819909f, - -0.679852f,0.0f, 0.733349f, - -0.0967772f, -0.564254f, 0.819909f, - -0.253366f, -0.513375f, 0.819909f, - -0.210086f, -0.646578f, 0.733349f, - 0.506732f, -0.266405f, 0.819909f, - 0.409955f, -0.399607f, 0.819909f, - 0.550012f, -0.399607f, 0.733349f, - 0.846658f, -0.513375f, 0.140057f, - 0.776629f, -0.564254f, 0.280115f, - 0.749881f, -0.646578f, 0.140057f, - 0.986715f, 0.0823236f, -0.140057f, - 0.986715f, -0.0823236f, -0.140057f, - 0.959967f,0.0f, -0.280114f, - 0.776629f, 0.564254f, 0.280115f, - 0.846658f, 0.513375f, 0.140057f, - 0.749881f, 0.646578f, 0.140057f, - 0.226617f, 0.963861f, -0.140057f, - 0.383206f, 0.912982f, -0.140057f, - 0.296646f, 0.912982f, -0.280114f, - -0.296646f, 0.912982f, 0.280114f, - -0.226617f, 0.963861f, 0.140057f, - -0.383206f, 0.912982f, 0.140057f, - -0.846658f, 0.513375f, -0.140057f, - -0.749881f, 0.646578f, -0.140057f, - -0.776629f, 0.564254f, -0.280115f, - -0.959967f,0.0f, 0.280114f, - -0.986715f, 0.0823236f, 0.140057f, - -0.986715f, -0.0823236f, 0.140057f, - -0.749881f, -0.646578f, -0.140057f, - -0.846658f, -0.513375f, -0.140057f, - -0.776629f, -0.564254f, -0.280115f, - -0.296646f, -0.912982f, 0.280114f, - -0.383206f, -0.912982f, 0.140057f, - -0.226617f, -0.963861f, 0.140057f, - 0.383206f, -0.912982f, -0.140057f, - 0.226617f, -0.963861f, -0.140057f, - 0.296646f, -0.912982f, -0.280114f, - 0.566543f, 0.0823236f, -0.819909f, - 0.679852f,0.0f, -0.733349f, - 0.566543f, -0.0823236f, -0.819909f, - 0.210086f, 0.646578f, -0.733349f, - 0.253366f, 0.513375f, -0.819909f, - 0.0967772f, 0.564254f, -0.819909f, - 0.253366f, -0.513375f, -0.819909f, - 0.210086f, -0.646578f, -0.733349f, - 0.0967772f, -0.564254f, -0.819909f, - -0.550012f, 0.399607f, -0.733349f, - -0.409955f, 0.399607f, -0.819909f, - -0.506732f, 0.266405f, -0.819909f, - -0.550012f, -0.399607f, -0.733349f, - -0.506732f, -0.266405f, -0.819909f, - -0.409955f, -0.399607f, -0.819909f, - 0.36954f, 0.711282f, 0.597928f, - 0.341154f, 0.62024f, 0.70634f, - 0.479201f, 0.62024f, 0.621023f, - 0.181208f, 0.131655f, 0.974592f, - 0.32082f, 0.132792f, 0.937785f, - 0.225432f, 0.264083f, 0.937785f, - 0.790663f, 0.131655f, 0.597928f, - 0.737964f, 0.264083f, 0.621023f, - 0.695305f, 0.132792f, 0.70634f, - -0.562275f, 0.571252f, 0.597928f, - -0.48446f, 0.516122f, 0.70634f, - -0.441802f, 0.647412f, 0.621023f, - -0.0692153f, 0.213023f, 0.974592f, - -0.0271543f, 0.346153f, 0.937785f, - -0.181495f, 0.296005f, 0.937785f, - 0.119117f, 0.792649f, 0.597928f, - -0.0231141f, 0.783452f, 0.621023f, - 0.088568f, 0.70231f, 0.70634f, - -0.717045f, -0.358229f, 0.597928f, - -0.640567f, -0.301259f, 0.70634f, - -0.75225f, -0.220117f, 0.621023f, - -0.223985f,0.0f, 0.974592f, - -0.337602f, 0.0811419f, 0.937785f, - -0.337602f, -0.0811419f, 0.937785f, - -0.717045f, 0.358229f, 0.597928f, - -0.75225f, 0.220117f, 0.621023f, - -0.640567f, 0.301259f, 0.70634f, - 0.119117f, -0.792649f, 0.597928f, - 0.088568f, -0.70231f, 0.70634f, - -0.0231141f, -0.783452f, 0.621023f, - -0.0692153f, -0.213023f, 0.974592f, - -0.181495f, -0.296005f, 0.937785f, - -0.0271543f, -0.346153f, 0.937785f, - -0.562275f, -0.571252f, 0.597928f, - -0.441802f, -0.647412f, 0.621023f, - -0.48446f, -0.516122f, 0.70634f, - 0.790663f, -0.131655f, 0.597928f, - 0.695305f, -0.132792f, 0.70634f, - 0.737964f, -0.264083f, 0.621023f, - 0.181208f, -0.131655f, 0.974592f, - 0.225432f, -0.264083f, 0.937785f, - 0.32082f, -0.132792f, 0.937785f, - 0.36954f, -0.711282f, 0.597928f, - 0.479201f, -0.62024f, 0.621023f, - 0.341154f, -0.62024f, 0.70634f, - 0.78626f, -0.571252f, -0.235512f, - 0.848427f, -0.516122f, -0.11743f, - 0.753039f, -0.647412f, -0.11743f, - 0.902656f, -0.213023f, 0.373943f, - 0.850924f, -0.346153f, 0.395103f, - 0.919948f, -0.296005f, 0.257056f, - 0.481533f, -0.792649f, 0.373943f, - 0.565797f, -0.783452f, 0.257056f, - 0.592161f, -0.70231f, 0.395103f, - 0.855476f, 0.358229f, -0.373943f, - 0.91824f, 0.301259f, -0.257056f, - 0.891876f, 0.220117f, -0.395103f, - 0.971871f,0.0f, 0.235512f, - 0.989761f, -0.0811419f, 0.11743f, - 0.989761f, 0.0811419f, 0.11743f, - 0.855476f, -0.358229f, -0.373943f, - 0.891876f, -0.220117f, -0.395103f, - 0.91824f, -0.301259f, -0.257056f, - 0.481533f, 0.792649f, 0.373943f, - 0.592161f, 0.70231f, 0.395103f, - 0.565797f, 0.783452f, 0.257056f, - 0.902656f, 0.213023f, 0.373943f, - 0.919948f, 0.296005f, 0.257056f, - 0.850924f, 0.346153f, 0.395103f, - 0.78626f, 0.571252f, -0.235512f, - 0.753039f, 0.647412f, -0.11743f, - 0.848427f, 0.516122f, -0.11743f, - -0.0763393f, 0.924305f, -0.373943f, - -0.00276234f, 0.966393f, -0.257056f, - 0.066261f, 0.916244f, -0.395103f, - 0.300325f, 0.924305f, 0.235512f, - 0.383023f, 0.916244f, 0.11743f, - 0.228682f, 0.966393f, 0.11743f, - 0.605052f, 0.702907f, -0.373943f, - 0.484949f, 0.780204f, -0.395103f, - 0.570266f, 0.780204f, -0.257056f, - -0.605052f, 0.702907f, 0.373943f, - -0.484949f, 0.780204f, 0.395103f, - -0.570266f, 0.780204f, 0.257056f, - 0.0763393f, 0.924305f, 0.373943f, - 0.00276234f, 0.966393f, 0.257056f, - -0.066261f, 0.916244f, 0.395103f, - -0.300325f, 0.924305f, -0.235512f, - -0.383023f, 0.916244f, -0.11743f, - -0.228682f, 0.966393f, -0.11743f, - -0.902656f, 0.213023f, -0.373943f, - -0.919948f, 0.296005f, -0.257056f, - -0.850924f, 0.346153f, -0.395103f, - -0.78626f, 0.571252f, 0.235512f, - -0.753039f, 0.647412f, 0.11743f, - -0.848427f, 0.516122f, 0.11743f, - -0.481533f, 0.792649f, -0.373943f, - -0.592161f, 0.70231f, -0.395103f, - -0.565797f, 0.783452f, -0.257056f, - -0.855476f, -0.358229f, 0.373943f, - -0.891876f, -0.220117f, 0.395103f, - -0.91824f, -0.301259f, 0.257056f, - -0.855476f, 0.358229f, 0.373943f, - -0.91824f, 0.301259f, 0.257056f, - -0.891876f, 0.220117f, 0.395103f, - -0.971871f,0.0f, -0.235512f, - -0.989761f, -0.0811419f, -0.11743f, - -0.989761f, 0.0811419f, -0.11743f, - -0.481533f, -0.792649f, -0.373943f, - -0.565797f, -0.783452f, -0.257056f, - -0.592161f, -0.70231f, -0.395103f, - -0.78626f, -0.571252f, 0.235512f, - -0.848427f, -0.516122f, 0.11743f, - -0.753039f, -0.647412f, 0.11743f, - -0.902656f, -0.213023f, -0.373943f, - -0.850924f, -0.346153f, -0.395103f, - -0.919948f, -0.296005f, -0.257056f, - 0.0763393f, -0.924305f, 0.373943f, - -0.066261f, -0.916244f, 0.395103f, - 0.00276234f, -0.966393f, 0.257056f, - -0.605052f, -0.702907f, 0.373943f, - -0.570266f, -0.780204f, 0.257056f, - -0.484949f, -0.780204f, 0.395103f, - -0.300325f, -0.924305f, -0.235512f, - -0.228682f, -0.966393f, -0.11743f, - -0.383023f, -0.916244f, -0.11743f, - 0.605052f, -0.702907f, -0.373943f, - 0.570266f, -0.780204f, -0.257056f, - 0.484949f, -0.780204f, -0.395103f, - 0.300325f, -0.924305f, 0.235512f, - 0.228682f, -0.966393f, 0.11743f, - 0.383023f, -0.916244f, 0.11743f, - -0.0763393f, -0.924305f, -0.373943f, - 0.066261f, -0.916244f, -0.395103f, - -0.00276234f, -0.966393f, -0.257056f, - 0.223985f,0.0f, -0.974592f, - 0.337602f, 0.0811419f, -0.937785f, - 0.337602f, -0.0811419f, -0.937785f, - 0.717045f, 0.358229f, -0.597928f, - 0.75225f, 0.220117f, -0.621023f, - 0.640567f, 0.301259f, -0.70634f, - 0.717045f, -0.358229f, -0.597928f, - 0.640567f, -0.301259f, -0.70634f, - 0.75225f, -0.220117f, -0.621023f, - -0.119117f, 0.792649f, -0.597928f, - 0.0231141f, 0.783452f, -0.621023f, - -0.088568f, 0.70231f, -0.70634f, - 0.562275f, 0.571252f, -0.597928f, - 0.48446f, 0.516122f, -0.70634f, - 0.441802f, 0.647412f, -0.621023f, - 0.0692153f, 0.213023f, -0.974592f, - 0.0271543f, 0.346153f, -0.937785f, - 0.181495f, 0.296005f, -0.937785f, - 0.0692153f, -0.213023f, -0.974592f, - 0.181495f, -0.296005f, -0.937785f, - 0.0271543f, -0.346153f, -0.937785f, - 0.562275f, -0.571252f, -0.597928f, - 0.441802f, -0.647412f, -0.621023f, - 0.48446f, -0.516122f, -0.70634f, - -0.119117f, -0.792649f, -0.597928f, - -0.088568f, -0.70231f, -0.70634f, - 0.0231141f, -0.783452f, -0.621023f, - -0.790663f, 0.131655f, -0.597928f, - -0.737964f, 0.264083f, -0.621023f, - -0.695305f, 0.132792f, -0.70634f, - -0.36954f, 0.711282f, -0.597928f, - -0.341154f, 0.62024f, -0.70634f, - -0.479201f, 0.62024f, -0.621023f, - -0.181208f, 0.131655f, -0.974592f, - -0.32082f, 0.132792f, -0.937785f, - -0.225432f, 0.264083f, -0.937785f, - -0.36954f, -0.711282f, -0.597928f, - -0.479201f, -0.62024f, -0.621023f, - -0.341154f, -0.62024f, -0.70634f, - -0.790663f, -0.131655f, -0.597928f, - -0.695305f, -0.132792f, -0.70634f, - -0.737964f, -0.264083f, -0.621023f, - -0.181208f, -0.131655f, -0.974592f, - -0.225432f, -0.264083f, -0.937785f, - -0.32082f, -0.132792f, -0.937785f - }; - - static GLushort SPHERE_MODEL_TRIANGLES[SPHERE_MODEL_NB_TRIANGLES*3] = { - 403, 404, 402, - 406, 407, 405, - 409, 410, 408, - 412, 413, 411, - 415, 416, 414, - 418, 419, 417, - 421, 422, 420, - 424, 425, 423, - 427, 428, 426, - 430, 431, 429, - 433, 434, 432, - 436, 437, 435, - 439, 440, 438, - 442, 443, 441, - 445, 446, 444, - 448, 449, 447, - 451, 452, 450, - 454, 455, 453, - 457, 458, 456, - 460, 461, 459, - 463, 464, 462, - 466, 467, 465, - 469, 470, 468, - 472, 473, 471, - 475, 476, 474, - 478, 479, 477, - 481, 482, 480, - 484, 485, 483, - 487, 488, 486, - 490, 491, 489, - 493, 494, 492, - 496, 497, 495, - 499, 500, 498, - 502, 503, 501, - 505, 506, 504, - 508, 509, 507, - 511, 512, 510, - 514, 515, 513, - 517, 518, 516, - 520, 521, 519, - 523, 524, 522, - 526, 527, 525, - 529, 530, 528, - 532, 533, 531, - 535, 536, 534, - 538, 539, 537, - 541, 542, 540, - 544, 545, 543, - 547, 548, 546, - 550, 551, 549, - 553, 554, 552, - 556, 557, 555, - 559, 560, 558, - 562, 563, 561, - 565, 566, 564, - 568, 569, 567, - 571, 572, 570, - 574, 575, 573, - 577, 578, 576, - 580, 581, 579, - 583, 584, 582, - 586, 587, 585, - 589, 590, 588, - 592, 593, 591, - 595, 596, 594, - 598, 599, 597, - 601, 602, 600, - 604, 605, 603, - 607, 608, 606, - 610, 611, 609, - 613, 614, 612, - 616, 617, 615, - 619, 620, 618, - 622, 623, 621, - 625, 626, 624, - 628, 629, 627, - 631, 632, 630, - 634, 635, 633, - 637, 638, 636, - 640, 641, 639, - 343, 402, 222, - 344, 403, 223, - 342, 404, 224, - 346, 405, 225, - 347, 406, 226, - 345, 407, 227, - 349, 408, 228, - 350, 409, 229, - 348, 410, 230, - 352, 411, 231, - 353, 412, 232, - 351, 413, 233, - 355, 414, 234, - 356, 415, 235, - 354, 416, 236, - 358, 417, 237, - 359, 418, 238, - 357, 419, 239, - 361, 420, 240, - 362, 421, 241, - 360, 422, 242, - 364, 423, 243, - 365, 424, 244, - 363, 425, 245, - 367, 426, 246, - 368, 427, 247, - 366, 428, 248, - 370, 429, 249, - 371, 430, 250, - 369, 431, 251, - 373, 432, 252, - 374, 433, 253, - 372, 434, 254, - 376, 435, 255, - 377, 436, 256, - 375, 437, 257, - 379, 438, 258, - 380, 439, 259, - 378, 440, 260, - 382, 441, 261, - 383, 442, 262, - 381, 443, 263, - 385, 444, 264, - 386, 445, 265, - 384, 446, 266, - 388, 447, 267, - 389, 448, 268, - 387, 449, 269, - 391, 450, 270, - 392, 451, 271, - 390, 452, 272, - 394, 453, 273, - 395, 454, 274, - 393, 455, 275, - 397, 456, 276, - 398, 457, 277, - 396, 458, 278, - 400, 459, 279, - 401, 460, 280, - 399, 461, 281, - 164, 462, 313, - 222, 463, 284, - 193, 464, 342, - 162, 465, 314, - 223, 466, 282, - 194, 467, 343, - 163, 468, 312, - 224, 469, 283, - 192, 470, 344, - 166, 471, 315, - 225, 472, 286, - 195, 473, 345, - 314, 474, 316, - 226, 475, 194, - 196, 476, 346, - 165, 477, 164, - 227, 478, 285, - 284, 479, 347, - 168, 480, 317, - 228, 481, 288, - 197, 482, 348, - 316, 483, 318, - 229, 484, 196, - 198, 485, 349, - 167, 486, 166, - 230, 487, 287, - 286, 488, 350, - 170, 489, 319, - 231, 490, 290, - 199, 491, 351, - 318, 492, 320, - 232, 493, 198, - 200, 494, 352, - 169, 495, 168, - 233, 496, 289, - 288, 497, 353, - 312, 498, 321, - 234, 499, 192, - 201, 500, 354, - 320, 501, 162, - 235, 502, 200, - 282, 503, 355, - 171, 504, 170, - 236, 505, 291, - 290, 506, 356, - 173, 507, 322, - 237, 508, 293, - 202, 509, 357, - 321, 510, 323, - 238, 511, 201, - 203, 512, 358, - 172, 513, 171, - 239, 514, 292, - 291, 515, 359, - 175, 516, 324, - 240, 517, 295, - 204, 518, 360, - 323, 519, 325, - 241, 520, 203, - 205, 521, 361, - 174, 522, 173, - 242, 523, 294, - 293, 524, 362, - 313, 525, 326, - 243, 526, 193, - 206, 527, 363, - 325, 528, 163, - 244, 529, 205, - 283, 530, 364, - 176, 531, 175, - 245, 532, 296, - 295, 533, 365, - 178, 534, 327, - 246, 535, 298, - 207, 536, 366, - 326, 537, 328, - 247, 538, 206, - 208, 539, 367, - 177, 540, 176, - 248, 541, 297, - 296, 542, 368, - 315, 543, 329, - 249, 544, 195, - 209, 545, 369, - 328, 546, 165, - 250, 547, 208, - 285, 548, 370, - 179, 549, 178, - 251, 550, 299, - 298, 551, 371, - 181, 552, 330, - 252, 553, 301, - 210, 554, 372, - 329, 555, 331, - 253, 556, 209, - 211, 557, 373, - 180, 558, 179, - 254, 559, 300, - 299, 560, 374, - 317, 561, 332, - 255, 562, 197, - 212, 563, 375, - 331, 564, 167, - 256, 565, 211, - 287, 566, 376, - 182, 567, 181, - 257, 568, 302, - 301, 569, 377, - 184, 570, 333, - 258, 571, 304, - 213, 572, 378, - 332, 573, 334, - 259, 574, 212, - 214, 575, 379, - 183, 576, 182, - 260, 577, 303, - 302, 578, 380, - 319, 579, 335, - 261, 580, 199, - 215, 581, 381, - 334, 582, 169, - 262, 583, 214, - 289, 584, 382, - 185, 585, 184, - 263, 586, 305, - 304, 587, 383, - 322, 588, 336, - 264, 589, 202, - 216, 590, 384, - 335, 591, 172, - 265, 592, 215, - 292, 593, 385, - 186, 594, 185, - 266, 595, 306, - 305, 596, 386, - 188, 597, 337, - 267, 598, 308, - 217, 599, 387, - 324, 600, 338, - 268, 601, 204, - 218, 602, 388, - 187, 603, 174, - 269, 604, 307, - 294, 605, 389, - 327, 606, 339, - 270, 607, 207, - 219, 608, 390, - 338, 609, 177, - 271, 610, 218, - 297, 611, 391, - 189, 612, 188, - 272, 613, 309, - 308, 614, 392, - 337, 615, 340, - 273, 616, 217, - 220, 617, 393, - 336, 618, 187, - 274, 619, 216, - 307, 620, 394, - 190, 621, 186, - 275, 622, 310, - 306, 623, 395, - 330, 624, 341, - 276, 625, 210, - 221, 626, 396, - 339, 627, 180, - 277, 628, 219, - 300, 629, 397, - 191, 630, 189, - 278, 631, 311, - 309, 632, 398, - 333, 633, 190, - 279, 634, 213, - 310, 635, 399, - 341, 636, 183, - 280, 637, 221, - 303, 638, 400, - 340, 639, 191, - 281, 640, 220, - 311, 641, 401, - 103, 403, 402, - 104, 404, 403, - 102, 402, 404, - 106, 406, 405, - 107, 407, 406, - 105, 405, 407, - 109, 409, 408, - 110, 410, 409, - 108, 408, 410, - 112, 412, 411, - 113, 413, 412, - 111, 411, 413, - 115, 415, 414, - 116, 416, 415, - 114, 414, 416, - 118, 418, 417, - 119, 419, 418, - 117, 417, 419, - 121, 421, 420, - 122, 422, 421, - 120, 420, 422, - 124, 424, 423, - 125, 425, 424, - 123, 423, 425, - 127, 427, 426, - 128, 428, 427, - 126, 426, 428, - 130, 430, 429, - 131, 431, 430, - 129, 429, 431, - 133, 433, 432, - 134, 434, 433, - 132, 432, 434, - 136, 436, 435, - 137, 437, 436, - 135, 435, 437, - 139, 439, 438, - 140, 440, 439, - 138, 438, 440, - 142, 442, 441, - 143, 443, 442, - 141, 441, 443, - 145, 445, 444, - 146, 446, 445, - 144, 444, 446, - 148, 448, 447, - 149, 449, 448, - 147, 447, 449, - 151, 451, 450, - 152, 452, 451, - 150, 450, 452, - 154, 454, 453, - 155, 455, 454, - 153, 453, 455, - 157, 457, 456, - 158, 458, 457, - 156, 456, 458, - 160, 460, 459, - 161, 461, 460, - 159, 459, 461, - 44, 463, 462, - 102, 464, 463, - 73, 462, 464, - 42, 466, 465, - 103, 467, 466, - 74, 465, 467, - 43, 469, 468, - 104, 470, 469, - 72, 468, 470, - 46, 472, 471, - 105, 473, 472, - 75, 471, 473, - 74, 475, 474, - 106, 476, 475, - 76, 474, 476, - 45, 478, 477, - 107, 479, 478, - 44, 477, 479, - 48, 481, 480, - 108, 482, 481, - 77, 480, 482, - 76, 484, 483, - 109, 485, 484, - 78, 483, 485, - 47, 487, 486, - 110, 488, 487, - 46, 486, 488, - 50, 490, 489, - 111, 491, 490, - 79, 489, 491, - 78, 493, 492, - 112, 494, 493, - 80, 492, 494, - 49, 496, 495, - 113, 497, 496, - 48, 495, 497, - 72, 499, 498, - 114, 500, 499, - 81, 498, 500, - 80, 502, 501, - 115, 503, 502, - 42, 501, 503, - 51, 505, 504, - 116, 506, 505, - 50, 504, 506, - 53, 508, 507, - 117, 509, 508, - 82, 507, 509, - 81, 511, 510, - 118, 512, 511, - 83, 510, 512, - 52, 514, 513, - 119, 515, 514, - 51, 513, 515, - 55, 517, 516, - 120, 518, 517, - 84, 516, 518, - 83, 520, 519, - 121, 521, 520, - 85, 519, 521, - 54, 523, 522, - 122, 524, 523, - 53, 522, 524, - 73, 526, 525, - 123, 527, 526, - 86, 525, 527, - 85, 529, 528, - 124, 530, 529, - 43, 528, 530, - 56, 532, 531, - 125, 533, 532, - 55, 531, 533, - 58, 535, 534, - 126, 536, 535, - 87, 534, 536, - 86, 538, 537, - 127, 539, 538, - 88, 537, 539, - 57, 541, 540, - 128, 542, 541, - 56, 540, 542, - 75, 544, 543, - 129, 545, 544, - 89, 543, 545, - 88, 547, 546, - 130, 548, 547, - 45, 546, 548, - 59, 550, 549, - 131, 551, 550, - 58, 549, 551, - 61, 553, 552, - 132, 554, 553, - 90, 552, 554, - 89, 556, 555, - 133, 557, 556, - 91, 555, 557, - 60, 559, 558, - 134, 560, 559, - 59, 558, 560, - 77, 562, 561, - 135, 563, 562, - 92, 561, 563, - 91, 565, 564, - 136, 566, 565, - 47, 564, 566, - 62, 568, 567, - 137, 569, 568, - 61, 567, 569, - 64, 571, 570, - 138, 572, 571, - 93, 570, 572, - 92, 574, 573, - 139, 575, 574, - 94, 573, 575, - 63, 577, 576, - 140, 578, 577, - 62, 576, 578, - 79, 580, 579, - 141, 581, 580, - 95, 579, 581, - 94, 583, 582, - 142, 584, 583, - 49, 582, 584, - 65, 586, 585, - 143, 587, 586, - 64, 585, 587, - 82, 589, 588, - 144, 590, 589, - 96, 588, 590, - 95, 592, 591, - 145, 593, 592, - 52, 591, 593, - 66, 595, 594, - 146, 596, 595, - 65, 594, 596, - 68, 598, 597, - 147, 599, 598, - 97, 597, 599, - 84, 601, 600, - 148, 602, 601, - 98, 600, 602, - 67, 604, 603, - 149, 605, 604, - 54, 603, 605, - 87, 607, 606, - 150, 608, 607, - 99, 606, 608, - 98, 610, 609, - 151, 611, 610, - 57, 609, 611, - 69, 613, 612, - 152, 614, 613, - 68, 612, 614, - 97, 616, 615, - 153, 617, 616, - 100, 615, 617, - 96, 619, 618, - 154, 620, 619, - 67, 618, 620, - 70, 622, 621, - 155, 623, 622, - 66, 621, 623, - 90, 625, 624, - 156, 626, 625, - 101, 624, 626, - 99, 628, 627, - 157, 629, 628, - 60, 627, 629, - 71, 631, 630, - 158, 632, 631, - 69, 630, 632, - 93, 634, 633, - 159, 635, 634, - 70, 633, 635, - 101, 637, 636, - 160, 638, 637, - 63, 636, 638, - 100, 640, 639, - 161, 641, 640, - 71, 639, 641, - 14, 343, 222, - 103, 402, 343, - 102, 222, 402, - 12, 344, 223, - 104, 403, 344, - 103, 223, 403, - 13, 342, 224, - 102, 404, 342, - 104, 224, 404, - 16, 346, 225, - 106, 405, 346, - 105, 225, 405, - 14, 347, 226, - 107, 406, 347, - 106, 226, 406, - 15, 345, 227, - 105, 407, 345, - 107, 227, 407, - 18, 349, 228, - 109, 408, 349, - 108, 228, 408, - 16, 350, 229, - 110, 409, 350, - 109, 229, 409, - 17, 348, 230, - 108, 410, 348, - 110, 230, 410, - 20, 352, 231, - 112, 411, 352, - 111, 231, 411, - 18, 353, 232, - 113, 412, 353, - 112, 232, 412, - 19, 351, 233, - 111, 413, 351, - 113, 233, 413, - 12, 355, 234, - 115, 414, 355, - 114, 234, 414, - 20, 356, 235, - 116, 415, 356, - 115, 235, 415, - 21, 354, 236, - 114, 416, 354, - 116, 236, 416, - 23, 358, 237, - 118, 417, 358, - 117, 237, 417, - 21, 359, 238, - 119, 418, 359, - 118, 238, 418, - 22, 357, 239, - 117, 419, 357, - 119, 239, 419, - 25, 361, 240, - 121, 420, 361, - 120, 240, 420, - 23, 362, 241, - 122, 421, 362, - 121, 241, 421, - 24, 360, 242, - 120, 422, 360, - 122, 242, 422, - 13, 364, 243, - 124, 423, 364, - 123, 243, 423, - 25, 365, 244, - 125, 424, 365, - 124, 244, 424, - 26, 363, 245, - 123, 425, 363, - 125, 245, 425, - 28, 367, 246, - 127, 426, 367, - 126, 246, 426, - 26, 368, 247, - 128, 427, 368, - 127, 247, 427, - 27, 366, 248, - 126, 428, 366, - 128, 248, 428, - 15, 370, 249, - 130, 429, 370, - 129, 249, 429, - 28, 371, 250, - 131, 430, 371, - 130, 250, 430, - 29, 369, 251, - 129, 431, 369, - 131, 251, 431, - 31, 373, 252, - 133, 432, 373, - 132, 252, 432, - 29, 374, 253, - 134, 433, 374, - 133, 253, 433, - 30, 372, 254, - 132, 434, 372, - 134, 254, 434, - 17, 376, 255, - 136, 435, 376, - 135, 255, 435, - 31, 377, 256, - 137, 436, 377, - 136, 256, 436, - 32, 375, 257, - 135, 437, 375, - 137, 257, 437, - 34, 379, 258, - 139, 438, 379, - 138, 258, 438, - 32, 380, 259, - 140, 439, 380, - 139, 259, 439, - 33, 378, 260, - 138, 440, 378, - 140, 260, 440, - 19, 382, 261, - 142, 441, 382, - 141, 261, 441, - 34, 383, 262, - 143, 442, 383, - 142, 262, 442, - 35, 381, 263, - 141, 443, 381, - 143, 263, 443, - 22, 385, 264, - 145, 444, 385, - 144, 264, 444, - 35, 386, 265, - 146, 445, 386, - 145, 265, 445, - 36, 384, 266, - 144, 446, 384, - 146, 266, 446, - 38, 388, 267, - 148, 447, 388, - 147, 267, 447, - 24, 389, 268, - 149, 448, 389, - 148, 268, 448, - 37, 387, 269, - 147, 449, 387, - 149, 269, 449, - 27, 391, 270, - 151, 450, 391, - 150, 270, 450, - 38, 392, 271, - 152, 451, 392, - 151, 271, 451, - 39, 390, 272, - 150, 452, 390, - 152, 272, 452, - 37, 394, 273, - 154, 453, 394, - 153, 273, 453, - 36, 395, 274, - 155, 454, 395, - 154, 274, 454, - 40, 393, 275, - 153, 455, 393, - 155, 275, 455, - 30, 397, 276, - 157, 456, 397, - 156, 276, 456, - 39, 398, 277, - 158, 457, 398, - 157, 277, 457, - 41, 396, 278, - 156, 458, 396, - 158, 278, 458, - 33, 400, 279, - 160, 459, 400, - 159, 279, 459, - 41, 401, 280, - 161, 460, 401, - 160, 280, 460, - 40, 399, 281, - 159, 461, 399, - 161, 281, 461, - 2, 164, 313, - 44, 462, 164, - 73, 313, 462, - 14, 222, 284, - 102, 463, 222, - 44, 284, 463, - 13, 193, 342, - 73, 464, 193, - 102, 342, 464, - 0, 162, 314, - 42, 465, 162, - 74, 314, 465, - 12, 223, 282, - 103, 466, 223, - 42, 282, 466, - 14, 194, 343, - 74, 467, 194, - 103, 343, 467, - 1, 163, 312, - 43, 468, 163, - 72, 312, 468, - 13, 224, 283, - 104, 469, 224, - 43, 283, 469, - 12, 192, 344, - 72, 470, 192, - 104, 344, 470, - 3, 166, 315, - 46, 471, 166, - 75, 315, 471, - 16, 225, 286, - 105, 472, 225, - 46, 286, 472, - 15, 195, 345, - 75, 473, 195, - 105, 345, 473, - 0, 314, 316, - 74, 474, 314, - 76, 316, 474, - 14, 226, 194, - 106, 475, 226, - 74, 194, 475, - 16, 196, 346, - 76, 476, 196, - 106, 346, 476, - 2, 165, 164, - 45, 477, 165, - 44, 164, 477, - 15, 227, 285, - 107, 478, 227, - 45, 285, 478, - 14, 284, 347, - 44, 479, 284, - 107, 347, 479, - 4, 168, 317, - 48, 480, 168, - 77, 317, 480, - 18, 228, 288, - 108, 481, 228, - 48, 288, 481, - 17, 197, 348, - 77, 482, 197, - 108, 348, 482, - 0, 316, 318, - 76, 483, 316, - 78, 318, 483, - 16, 229, 196, - 109, 484, 229, - 76, 196, 484, - 18, 198, 349, - 78, 485, 198, - 109, 349, 485, - 3, 167, 166, - 47, 486, 167, - 46, 166, 486, - 17, 230, 287, - 110, 487, 230, - 47, 287, 487, - 16, 286, 350, - 46, 488, 286, - 110, 350, 488, - 5, 170, 319, - 50, 489, 170, - 79, 319, 489, - 20, 231, 290, - 111, 490, 231, - 50, 290, 490, - 19, 199, 351, - 79, 491, 199, - 111, 351, 491, - 0, 318, 320, - 78, 492, 318, - 80, 320, 492, - 18, 232, 198, - 112, 493, 232, - 78, 198, 493, - 20, 200, 352, - 80, 494, 200, - 112, 352, 494, - 4, 169, 168, - 49, 495, 169, - 48, 168, 495, - 19, 233, 289, - 113, 496, 233, - 49, 289, 496, - 18, 288, 353, - 48, 497, 288, - 113, 353, 497, - 1, 312, 321, - 72, 498, 312, - 81, 321, 498, - 12, 234, 192, - 114, 499, 234, - 72, 192, 499, - 21, 201, 354, - 81, 500, 201, - 114, 354, 500, - 0, 320, 162, - 80, 501, 320, - 42, 162, 501, - 20, 235, 200, - 115, 502, 235, - 80, 200, 502, - 12, 282, 355, - 42, 503, 282, - 115, 355, 503, - 5, 171, 170, - 51, 504, 171, - 50, 170, 504, - 21, 236, 291, - 116, 505, 236, - 51, 291, 505, - 20, 290, 356, - 50, 506, 290, - 116, 356, 506, - 7, 173, 322, - 53, 507, 173, - 82, 322, 507, - 23, 237, 293, - 117, 508, 237, - 53, 293, 508, - 22, 202, 357, - 82, 509, 202, - 117, 357, 509, - 1, 321, 323, - 81, 510, 321, - 83, 323, 510, - 21, 238, 201, - 118, 511, 238, - 81, 201, 511, - 23, 203, 358, - 83, 512, 203, - 118, 358, 512, - 5, 172, 171, - 52, 513, 172, - 51, 171, 513, - 22, 239, 292, - 119, 514, 239, - 52, 292, 514, - 21, 291, 359, - 51, 515, 291, - 119, 359, 515, - 6, 175, 324, - 55, 516, 175, - 84, 324, 516, - 25, 240, 295, - 120, 517, 240, - 55, 295, 517, - 24, 204, 360, - 84, 518, 204, - 120, 360, 518, - 1, 323, 325, - 83, 519, 323, - 85, 325, 519, - 23, 241, 203, - 121, 520, 241, - 83, 203, 520, - 25, 205, 361, - 85, 521, 205, - 121, 361, 521, - 7, 174, 173, - 54, 522, 174, - 53, 173, 522, - 24, 242, 294, - 122, 523, 242, - 54, 294, 523, - 23, 293, 362, - 53, 524, 293, - 122, 362, 524, - 2, 313, 326, - 73, 525, 313, - 86, 326, 525, - 13, 243, 193, - 123, 526, 243, - 73, 193, 526, - 26, 206, 363, - 86, 527, 206, - 123, 363, 527, - 1, 325, 163, - 85, 528, 325, - 43, 163, 528, - 25, 244, 205, - 124, 529, 244, - 85, 205, 529, - 13, 283, 364, - 43, 530, 283, - 124, 364, 530, - 6, 176, 175, - 56, 531, 176, - 55, 175, 531, - 26, 245, 296, - 125, 532, 245, - 56, 296, 532, - 25, 295, 365, - 55, 533, 295, - 125, 365, 533, - 8, 178, 327, - 58, 534, 178, - 87, 327, 534, - 28, 246, 298, - 126, 535, 246, - 58, 298, 535, - 27, 207, 366, - 87, 536, 207, - 126, 366, 536, - 2, 326, 328, - 86, 537, 326, - 88, 328, 537, - 26, 247, 206, - 127, 538, 247, - 86, 206, 538, - 28, 208, 367, - 88, 539, 208, - 127, 367, 539, - 6, 177, 176, - 57, 540, 177, - 56, 176, 540, - 27, 248, 297, - 128, 541, 248, - 57, 297, 541, - 26, 296, 368, - 56, 542, 296, - 128, 368, 542, - 3, 315, 329, - 75, 543, 315, - 89, 329, 543, - 15, 249, 195, - 129, 544, 249, - 75, 195, 544, - 29, 209, 369, - 89, 545, 209, - 129, 369, 545, - 2, 328, 165, - 88, 546, 328, - 45, 165, 546, - 28, 250, 208, - 130, 547, 250, - 88, 208, 547, - 15, 285, 370, - 45, 548, 285, - 130, 370, 548, - 8, 179, 178, - 59, 549, 179, - 58, 178, 549, - 29, 251, 299, - 131, 550, 251, - 59, 299, 550, - 28, 298, 371, - 58, 551, 298, - 131, 371, 551, - 9, 181, 330, - 61, 552, 181, - 90, 330, 552, - 31, 252, 301, - 132, 553, 252, - 61, 301, 553, - 30, 210, 372, - 90, 554, 210, - 132, 372, 554, - 3, 329, 331, - 89, 555, 329, - 91, 331, 555, - 29, 253, 209, - 133, 556, 253, - 89, 209, 556, - 31, 211, 373, - 91, 557, 211, - 133, 373, 557, - 8, 180, 179, - 60, 558, 180, - 59, 179, 558, - 30, 254, 300, - 134, 559, 254, - 60, 300, 559, - 29, 299, 374, - 59, 560, 299, - 134, 374, 560, - 4, 317, 332, - 77, 561, 317, - 92, 332, 561, - 17, 255, 197, - 135, 562, 255, - 77, 197, 562, - 32, 212, 375, - 92, 563, 212, - 135, 375, 563, - 3, 331, 167, - 91, 564, 331, - 47, 167, 564, - 31, 256, 211, - 136, 565, 256, - 91, 211, 565, - 17, 287, 376, - 47, 566, 287, - 136, 376, 566, - 9, 182, 181, - 62, 567, 182, - 61, 181, 567, - 32, 257, 302, - 137, 568, 257, - 62, 302, 568, - 31, 301, 377, - 61, 569, 301, - 137, 377, 569, - 10, 184, 333, - 64, 570, 184, - 93, 333, 570, - 34, 258, 304, - 138, 571, 258, - 64, 304, 571, - 33, 213, 378, - 93, 572, 213, - 138, 378, 572, - 4, 332, 334, - 92, 573, 332, - 94, 334, 573, - 32, 259, 212, - 139, 574, 259, - 92, 212, 574, - 34, 214, 379, - 94, 575, 214, - 139, 379, 575, - 9, 183, 182, - 63, 576, 183, - 62, 182, 576, - 33, 260, 303, - 140, 577, 260, - 63, 303, 577, - 32, 302, 380, - 62, 578, 302, - 140, 380, 578, - 5, 319, 335, - 79, 579, 319, - 95, 335, 579, - 19, 261, 199, - 141, 580, 261, - 79, 199, 580, - 35, 215, 381, - 95, 581, 215, - 141, 381, 581, - 4, 334, 169, - 94, 582, 334, - 49, 169, 582, - 34, 262, 214, - 142, 583, 262, - 94, 214, 583, - 19, 289, 382, - 49, 584, 289, - 142, 382, 584, - 10, 185, 184, - 65, 585, 185, - 64, 184, 585, - 35, 263, 305, - 143, 586, 263, - 65, 305, 586, - 34, 304, 383, - 64, 587, 304, - 143, 383, 587, - 7, 322, 336, - 82, 588, 322, - 96, 336, 588, - 22, 264, 202, - 144, 589, 264, - 82, 202, 589, - 36, 216, 384, - 96, 590, 216, - 144, 384, 590, - 5, 335, 172, - 95, 591, 335, - 52, 172, 591, - 35, 265, 215, - 145, 592, 265, - 95, 215, 592, - 22, 292, 385, - 52, 593, 292, - 145, 385, 593, - 10, 186, 185, - 66, 594, 186, - 65, 185, 594, - 36, 266, 306, - 146, 595, 266, - 66, 306, 595, - 35, 305, 386, - 65, 596, 305, - 146, 386, 596, - 11, 188, 337, - 68, 597, 188, - 97, 337, 597, - 38, 267, 308, - 147, 598, 267, - 68, 308, 598, - 37, 217, 387, - 97, 599, 217, - 147, 387, 599, - 6, 324, 338, - 84, 600, 324, - 98, 338, 600, - 24, 268, 204, - 148, 601, 268, - 84, 204, 601, - 38, 218, 388, - 98, 602, 218, - 148, 388, 602, - 7, 187, 174, - 67, 603, 187, - 54, 174, 603, - 37, 269, 307, - 149, 604, 269, - 67, 307, 604, - 24, 294, 389, - 54, 605, 294, - 149, 389, 605, - 8, 327, 339, - 87, 606, 327, - 99, 339, 606, - 27, 270, 207, - 150, 607, 270, - 87, 207, 607, - 39, 219, 390, - 99, 608, 219, - 150, 390, 608, - 6, 338, 177, - 98, 609, 338, - 57, 177, 609, - 38, 271, 218, - 151, 610, 271, - 98, 218, 610, - 27, 297, 391, - 57, 611, 297, - 151, 391, 611, - 11, 189, 188, - 69, 612, 189, - 68, 188, 612, - 39, 272, 309, - 152, 613, 272, - 69, 309, 613, - 38, 308, 392, - 68, 614, 308, - 152, 392, 614, - 11, 337, 340, - 97, 615, 337, - 100, 340, 615, - 37, 273, 217, - 153, 616, 273, - 97, 217, 616, - 40, 220, 393, - 100, 617, 220, - 153, 393, 617, - 7, 336, 187, - 96, 618, 336, - 67, 187, 618, - 36, 274, 216, - 154, 619, 274, - 96, 216, 619, - 37, 307, 394, - 67, 620, 307, - 154, 394, 620, - 10, 190, 186, - 70, 621, 190, - 66, 186, 621, - 40, 275, 310, - 155, 622, 275, - 70, 310, 622, - 36, 306, 395, - 66, 623, 306, - 155, 395, 623, - 9, 330, 341, - 90, 624, 330, - 101, 341, 624, - 30, 276, 210, - 156, 625, 276, - 90, 210, 625, - 41, 221, 396, - 101, 626, 221, - 156, 396, 626, - 8, 339, 180, - 99, 627, 339, - 60, 180, 627, - 39, 277, 219, - 157, 628, 277, - 99, 219, 628, - 30, 300, 397, - 60, 629, 300, - 157, 397, 629, - 11, 191, 189, - 71, 630, 191, - 69, 189, 630, - 41, 278, 311, - 158, 631, 278, - 71, 311, 631, - 39, 309, 398, - 69, 632, 309, - 158, 398, 632, - 10, 333, 190, - 93, 633, 333, - 70, 190, 633, - 33, 279, 213, - 159, 634, 279, - 93, 213, 634, - 40, 310, 399, - 70, 635, 310, - 159, 399, 635, - 9, 341, 183, - 101, 636, 341, - 63, 183, 636, - 41, 280, 221, - 160, 637, 280, - 101, 221, 637, - 33, 303, 400, - 63, 638, 303, - 160, 400, 638, - 11, 340, 191, - 100, 639, 340, - 71, 191, 639, - 40, 281, 220, - 161, 640, 281, - 100, 220, 640, - 41, 311, 401, - 71, 641, 311, - 161, 401, 641 - }; - - GLuint result = glGenLists(1); - glNewList(result, GL_COMPILE); - - - glEnableClientState(GL_VERTEX_ARRAY); - glVertexPointer( - 3, - GL_FLOAT, - 0, - SPHERE_MODEL_PTS - ); - - glEnableClientState(GL_NORMAL_ARRAY); - glNormalPointer( - GL_FLOAT, - 0, - SPHERE_MODEL_PTS - ); - - glDrawElements( - GL_TRIANGLES, - GLsizei(SPHERE_MODEL_NB_TRIANGLES*3), - GL_UNSIGNED_SHORT, - SPHERE_MODEL_TRIANGLES - ); - - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_NORMAL_ARRAY); - - glEndList(); - return result; - } -#else - GLuint create_sphere_display_list() { - return 0; - } -#endif - -} diff --git a/src/lib/geogram_gfx/ImGui_ext/file_dialog.cpp b/src/lib/geogram_gfx/ImGui_ext/file_dialog.cpp index 83409ef0..edc69169 100644 --- a/src/lib/geogram_gfx/ImGui_ext/file_dialog.cpp +++ b/src/lib/geogram_gfx/ImGui_ext/file_dialog.cpp @@ -44,6 +44,7 @@ */ #include +#include #include #include #include @@ -90,6 +91,25 @@ namespace { } return result; } + + + /** + * \brief Converts an icon symbolic name and a label to a string. + * \param[in] icon_sym the symbolic name of the icon. + * \param[in] label the label to be displayed. + * \return a UTF8 string with the icon and label, or just the label + * if the icon font is not initialized. + */ + std::string icon_label(const char* icon_sym, const char* label) { + wchar_t str[2]; + str[0] = icon_wchar(icon_sym); + if(str[0] == '\0') { + return std::string(label); + } + str[1] = '\0'; + return GEO::String::wchar_to_UTF8(str) + " " + label; + } + } namespace GEO { @@ -335,16 +355,16 @@ namespace GEO { ImGuiWindowFlags_NoCollapse ); - if(ImGui::Button("parent")) { + if(ImGui::Button(icon_label("arrow-circle-up","parent").c_str())) { set_directory("../"); } ImGui::SameLine(); - if(ImGui::Button("home")) { + if(ImGui::Button(icon_label("home","home").c_str())) { set_directory(FileSystem::documents_directory()); update_files(); } ImGui::SameLine(); - if(ImGui::Button("refresh")) { + if(ImGui::Button(icon_label("recycle","refresh").c_str())) { update_files(); } @@ -367,6 +387,12 @@ namespace GEO { for(index_t i=0; i +#include +#include + +namespace { + +/* + Table below generated from metadata in FontAwesome distrib: + cat icons.yml | awk 'BEGIN { state = 0; } { + if(state == 0) { + key = $1; + gsub(":","",key); + state = 1; + } else if(state == 1 && $1 == "unicode:") { + printf("font_awesome_table[\"%s\"] = 0x%s;\n",key,$2) + state = 0; + } + }' +*/ + + std::map font_awesome_table; + void init_font_awesome_table(void); + + void init_font_awesome_table() { + static bool initialized = false; + if(initialized) { + return; + } + initialized = true; + font_awesome_table["500px"] = 0xf26e; + font_awesome_table["accessible-icon"] = 0xf368; + font_awesome_table["accusoft"] = 0xf369; + font_awesome_table["address-book"] = 0xf2b9; + font_awesome_table["address-card"] = 0xf2bb; + font_awesome_table["adjust"] = 0xf042; + font_awesome_table["adn"] = 0xf170; + font_awesome_table["adversal"] = 0xf36a; + font_awesome_table["affiliatetheme"] = 0xf36b; + font_awesome_table["air-freshener"] = 0xf5d0; + font_awesome_table["algolia"] = 0xf36c; + font_awesome_table["align-center"] = 0xf037; + font_awesome_table["align-justify"] = 0xf039; + font_awesome_table["align-left"] = 0xf036; + font_awesome_table["align-right"] = 0xf038; + font_awesome_table["allergies"] = 0xf461; + font_awesome_table["amazon"] = 0xf270; + font_awesome_table["amazon-pay"] = 0xf42c; + font_awesome_table["ambulance"] = 0xf0f9; + font_awesome_table["american-sign-language-interpreting"] = 0xf2a3; + font_awesome_table["amilia"] = 0xf36d; + font_awesome_table["anchor"] = 0xf13d; + font_awesome_table["android"] = 0xf17b; + font_awesome_table["angellist"] = 0xf209; + font_awesome_table["angle-double-down"] = 0xf103; + font_awesome_table["angle-double-left"] = 0xf100; + font_awesome_table["angle-double-right"] = 0xf101; + font_awesome_table["angle-double-up"] = 0xf102; + font_awesome_table["angle-down"] = 0xf107; + font_awesome_table["angle-left"] = 0xf104; + font_awesome_table["angle-right"] = 0xf105; + font_awesome_table["angle-up"] = 0xf106; + font_awesome_table["angry"] = 0xf556; + font_awesome_table["angrycreative"] = 0xf36e; + font_awesome_table["angular"] = 0xf420; + font_awesome_table["app-store"] = 0xf36f; + font_awesome_table["app-store-ios"] = 0xf370; + font_awesome_table["apper"] = 0xf371; + font_awesome_table["apple"] = 0xf179; + font_awesome_table["apple-alt"] = 0xf5d1; + font_awesome_table["apple-pay"] = 0xf415; + font_awesome_table["archive"] = 0xf187; + font_awesome_table["archway"] = 0xf557; + font_awesome_table["arrow-alt-circle-down"] = 0xf358; + font_awesome_table["arrow-alt-circle-left"] = 0xf359; + font_awesome_table["arrow-alt-circle-right"] = 0xf35a; + font_awesome_table["arrow-alt-circle-up"] = 0xf35b; + font_awesome_table["arrow-circle-down"] = 0xf0ab; + font_awesome_table["arrow-circle-left"] = 0xf0a8; + font_awesome_table["arrow-circle-right"] = 0xf0a9; + font_awesome_table["arrow-circle-up"] = 0xf0aa; + font_awesome_table["arrow-down"] = 0xf063; + font_awesome_table["arrow-left"] = 0xf060; + font_awesome_table["arrow-right"] = 0xf061; + font_awesome_table["arrow-up"] = 0xf062; + font_awesome_table["arrows-alt"] = 0xf0b2; + font_awesome_table["arrows-alt-h"] = 0xf337; + font_awesome_table["arrows-alt-v"] = 0xf338; + font_awesome_table["assistive-listening-systems"] = 0xf2a2; + font_awesome_table["asterisk"] = 0xf069; + font_awesome_table["asymmetrik"] = 0xf372; + font_awesome_table["at"] = 0xf1fa; + font_awesome_table["atlas"] = 0xf558; + font_awesome_table["atom"] = 0xf5d2; + font_awesome_table["audible"] = 0xf373; + font_awesome_table["audio-description"] = 0xf29e; + font_awesome_table["autoprefixer"] = 0xf41c; + font_awesome_table["avianex"] = 0xf374; + font_awesome_table["aviato"] = 0xf421; + font_awesome_table["award"] = 0xf559; + font_awesome_table["aws"] = 0xf375; + font_awesome_table["backspace"] = 0xf55a; + font_awesome_table["backward"] = 0xf04a; + font_awesome_table["balance-scale"] = 0xf24e; + font_awesome_table["ban"] = 0xf05e; + font_awesome_table["band-aid"] = 0xf462; + font_awesome_table["bandcamp"] = 0xf2d5; + font_awesome_table["barcode"] = 0xf02a; + font_awesome_table["bars"] = 0xf0c9; + font_awesome_table["baseball-ball"] = 0xf433; + font_awesome_table["basketball-ball"] = 0xf434; + font_awesome_table["bath"] = 0xf2cd; + font_awesome_table["battery-empty"] = 0xf244; + font_awesome_table["battery-full"] = 0xf240; + font_awesome_table["battery-half"] = 0xf242; + font_awesome_table["battery-quarter"] = 0xf243; + font_awesome_table["battery-three-quarters"] = 0xf241; + font_awesome_table["bed"] = 0xf236; + font_awesome_table["beer"] = 0xf0fc; + font_awesome_table["behance"] = 0xf1b4; + font_awesome_table["behance-square"] = 0xf1b5; + font_awesome_table["bell"] = 0xf0f3; + font_awesome_table["bell-slash"] = 0xf1f6; + font_awesome_table["bezier-curve"] = 0xf55b; + font_awesome_table["bicycle"] = 0xf206; + font_awesome_table["bimobject"] = 0xf378; + font_awesome_table["binoculars"] = 0xf1e5; + font_awesome_table["birthday-cake"] = 0xf1fd; + font_awesome_table["bitbucket"] = 0xf171; + font_awesome_table["bitcoin"] = 0xf379; + font_awesome_table["bity"] = 0xf37a; + font_awesome_table["black-tie"] = 0xf27e; + font_awesome_table["blackberry"] = 0xf37b; + font_awesome_table["blender"] = 0xf517; + font_awesome_table["blind"] = 0xf29d; + font_awesome_table["blogger"] = 0xf37c; + font_awesome_table["blogger-b"] = 0xf37d; + font_awesome_table["bluetooth"] = 0xf293; + font_awesome_table["bluetooth-b"] = 0xf294; + font_awesome_table["bold"] = 0xf032; + font_awesome_table["bolt"] = 0xf0e7; + font_awesome_table["bomb"] = 0xf1e2; + font_awesome_table["bone"] = 0xf5d7; + font_awesome_table["bong"] = 0xf55c; + font_awesome_table["book"] = 0xf02d; + font_awesome_table["book-open"] = 0xf518; + font_awesome_table["book-reader"] = 0xf5da; + font_awesome_table["bookmark"] = 0xf02e; + font_awesome_table["bowling-ball"] = 0xf436; + font_awesome_table["box"] = 0xf466; + font_awesome_table["box-open"] = 0xf49e; + font_awesome_table["boxes"] = 0xf468; + font_awesome_table["braille"] = 0xf2a1; + font_awesome_table["brain"] = 0xf5dc; + font_awesome_table["briefcase"] = 0xf0b1; + font_awesome_table["briefcase-medical"] = 0xf469; + font_awesome_table["broadcast-tower"] = 0xf519; + font_awesome_table["broom"] = 0xf51a; + font_awesome_table["brush"] = 0xf55d; + font_awesome_table["btc"] = 0xf15a; + font_awesome_table["bug"] = 0xf188; + font_awesome_table["building"] = 0xf1ad; + font_awesome_table["bullhorn"] = 0xf0a1; + font_awesome_table["bullseye"] = 0xf140; + font_awesome_table["burn"] = 0xf46a; + font_awesome_table["buromobelexperte"] = 0xf37f; + font_awesome_table["bus"] = 0xf207; + font_awesome_table["bus-alt"] = 0xf55e; + font_awesome_table["buysellads"] = 0xf20d; + font_awesome_table["calculator"] = 0xf1ec; + font_awesome_table["calendar"] = 0xf133; + font_awesome_table["calendar-alt"] = 0xf073; + font_awesome_table["calendar-check"] = 0xf274; + font_awesome_table["calendar-minus"] = 0xf272; + font_awesome_table["calendar-plus"] = 0xf271; + font_awesome_table["calendar-times"] = 0xf273; + font_awesome_table["camera"] = 0xf030; + font_awesome_table["camera-retro"] = 0xf083; + font_awesome_table["cannabis"] = 0xf55f; + font_awesome_table["capsules"] = 0xf46b; + font_awesome_table["car"] = 0xf1b9; + font_awesome_table["car-alt"] = 0xf5de; + font_awesome_table["car-battery"] = 0xf5df; + font_awesome_table["car-crash"] = 0xf5e1; + font_awesome_table["car-side"] = 0xf5e4; + font_awesome_table["caret-down"] = 0xf0d7; + font_awesome_table["caret-left"] = 0xf0d9; + font_awesome_table["caret-right"] = 0xf0da; + font_awesome_table["caret-square-down"] = 0xf150; + font_awesome_table["caret-square-left"] = 0xf191; + font_awesome_table["caret-square-right"] = 0xf152; + font_awesome_table["caret-square-up"] = 0xf151; + font_awesome_table["caret-up"] = 0xf0d8; + font_awesome_table["cart-arrow-down"] = 0xf218; + font_awesome_table["cart-plus"] = 0xf217; + font_awesome_table["cc-amazon-pay"] = 0xf42d; + font_awesome_table["cc-amex"] = 0xf1f3; + font_awesome_table["cc-apple-pay"] = 0xf416; + font_awesome_table["cc-diners-club"] = 0xf24c; + font_awesome_table["cc-discover"] = 0xf1f2; + font_awesome_table["cc-jcb"] = 0xf24b; + font_awesome_table["cc-mastercard"] = 0xf1f1; + font_awesome_table["cc-paypal"] = 0xf1f4; + font_awesome_table["cc-stripe"] = 0xf1f5; + font_awesome_table["cc-visa"] = 0xf1f0; + font_awesome_table["centercode"] = 0xf380; + font_awesome_table["certificate"] = 0xf0a3; + font_awesome_table["chalkboard"] = 0xf51b; + font_awesome_table["chalkboard-teacher"] = 0xf51c; + font_awesome_table["charging-station"] = 0xf5e7; + font_awesome_table["chart-area"] = 0xf1fe; + font_awesome_table["chart-bar"] = 0xf080; + font_awesome_table["chart-line"] = 0xf201; + font_awesome_table["chart-pie"] = 0xf200; + font_awesome_table["check"] = 0xf00c; + font_awesome_table["check-circle"] = 0xf058; + font_awesome_table["check-double"] = 0xf560; + font_awesome_table["check-square"] = 0xf14a; + font_awesome_table["chess"] = 0xf439; + font_awesome_table["chess-bishop"] = 0xf43a; + font_awesome_table["chess-board"] = 0xf43c; + font_awesome_table["chess-king"] = 0xf43f; + font_awesome_table["chess-knight"] = 0xf441; + font_awesome_table["chess-pawn"] = 0xf443; + font_awesome_table["chess-queen"] = 0xf445; + font_awesome_table["chess-rook"] = 0xf447; + font_awesome_table["chevron-circle-down"] = 0xf13a; + font_awesome_table["chevron-circle-left"] = 0xf137; + font_awesome_table["chevron-circle-right"] = 0xf138; + font_awesome_table["chevron-circle-up"] = 0xf139; + font_awesome_table["chevron-down"] = 0xf078; + font_awesome_table["chevron-left"] = 0xf053; + font_awesome_table["chevron-right"] = 0xf054; + font_awesome_table["chevron-up"] = 0xf077; + font_awesome_table["child"] = 0xf1ae; + font_awesome_table["chrome"] = 0xf268; + font_awesome_table["church"] = 0xf51d; + font_awesome_table["circle"] = 0xf111; + font_awesome_table["circle-notch"] = 0xf1ce; + font_awesome_table["clipboard"] = 0xf328; + font_awesome_table["clipboard-check"] = 0xf46c; + font_awesome_table["clipboard-list"] = 0xf46d; + font_awesome_table["clock"] = 0xf017; + font_awesome_table["clone"] = 0xf24d; + font_awesome_table["closed-captioning"] = 0xf20a; + font_awesome_table["cloud"] = 0xf0c2; + font_awesome_table["cloud-download-alt"] = 0xf381; + font_awesome_table["cloud-upload-alt"] = 0xf382; + font_awesome_table["cloudscale"] = 0xf383; + font_awesome_table["cloudsmith"] = 0xf384; + font_awesome_table["cloudversify"] = 0xf385; + font_awesome_table["cocktail"] = 0xf561; + font_awesome_table["code"] = 0xf121; + font_awesome_table["code-branch"] = 0xf126; + font_awesome_table["codepen"] = 0xf1cb; + font_awesome_table["codiepie"] = 0xf284; + font_awesome_table["coffee"] = 0xf0f4; + font_awesome_table["cog"] = 0xf013; + font_awesome_table["cogs"] = 0xf085; + font_awesome_table["coins"] = 0xf51e; + font_awesome_table["columns"] = 0xf0db; + font_awesome_table["comment"] = 0xf075; + font_awesome_table["comment-alt"] = 0xf27a; + font_awesome_table["comment-dots"] = 0xf4ad; + font_awesome_table["comment-slash"] = 0xf4b3; + font_awesome_table["comments"] = 0xf086; + font_awesome_table["compact-disc"] = 0xf51f; + font_awesome_table["compass"] = 0xf14e; + font_awesome_table["compress"] = 0xf066; + font_awesome_table["concierge-bell"] = 0xf562; + font_awesome_table["connectdevelop"] = 0xf20e; + font_awesome_table["contao"] = 0xf26d; + font_awesome_table["cookie"] = 0xf563; + font_awesome_table["cookie-bite"] = 0xf564; + font_awesome_table["copy"] = 0xf0c5; + font_awesome_table["copyright"] = 0xf1f9; + font_awesome_table["couch"] = 0xf4b8; + font_awesome_table["cpanel"] = 0xf388; + font_awesome_table["creative-commons"] = 0xf25e; + font_awesome_table["creative-commons-by"] = 0xf4e7; + font_awesome_table["creative-commons-nc"] = 0xf4e8; + font_awesome_table["creative-commons-nc-eu"] = 0xf4e9; + font_awesome_table["creative-commons-nc-jp"] = 0xf4ea; + font_awesome_table["creative-commons-nd"] = 0xf4eb; + font_awesome_table["creative-commons-pd"] = 0xf4ec; + font_awesome_table["creative-commons-pd-alt"] = 0xf4ed; + font_awesome_table["creative-commons-remix"] = 0xf4ee; + font_awesome_table["creative-commons-sa"] = 0xf4ef; + font_awesome_table["creative-commons-sampling"] = 0xf4f0; + font_awesome_table["creative-commons-sampling-plus"] = 0xf4f1; + font_awesome_table["creative-commons-share"] = 0xf4f2; + font_awesome_table["credit-card"] = 0xf09d; + font_awesome_table["crop"] = 0xf125; + font_awesome_table["crop-alt"] = 0xf565; + font_awesome_table["crosshairs"] = 0xf05b; + font_awesome_table["crow"] = 0xf520; + font_awesome_table["crown"] = 0xf521; + font_awesome_table["css3"] = 0xf13c; + font_awesome_table["css3-alt"] = 0xf38b; + font_awesome_table["cube"] = 0xf1b2; + font_awesome_table["cubes"] = 0xf1b3; + font_awesome_table["cut"] = 0xf0c4; + font_awesome_table["cuttlefish"] = 0xf38c; + font_awesome_table["d-and-d"] = 0xf38d; + font_awesome_table["dashcube"] = 0xf210; + font_awesome_table["database"] = 0xf1c0; + font_awesome_table["deaf"] = 0xf2a4; + font_awesome_table["delicious"] = 0xf1a5; + font_awesome_table["deploydog"] = 0xf38e; + font_awesome_table["deskpro"] = 0xf38f; + font_awesome_table["desktop"] = 0xf108; + font_awesome_table["deviantart"] = 0xf1bd; + font_awesome_table["diagnoses"] = 0xf470; + font_awesome_table["dice"] = 0xf522; + font_awesome_table["dice-five"] = 0xf523; + font_awesome_table["dice-four"] = 0xf524; + font_awesome_table["dice-one"] = 0xf525; + font_awesome_table["dice-six"] = 0xf526; + font_awesome_table["dice-three"] = 0xf527; + font_awesome_table["dice-two"] = 0xf528; + font_awesome_table["digg"] = 0xf1a6; + font_awesome_table["digital-ocean"] = 0xf391; + font_awesome_table["digital-tachograph"] = 0xf566; + font_awesome_table["directions"] = 0xf5eb; + font_awesome_table["discord"] = 0xf392; + font_awesome_table["discourse"] = 0xf393; + font_awesome_table["divide"] = 0xf529; + font_awesome_table["dizzy"] = 0xf567; + font_awesome_table["dna"] = 0xf471; + font_awesome_table["dochub"] = 0xf394; + font_awesome_table["docker"] = 0xf395; + font_awesome_table["dollar-sign"] = 0xf155; + font_awesome_table["dolly"] = 0xf472; + font_awesome_table["dolly-flatbed"] = 0xf474; + font_awesome_table["donate"] = 0xf4b9; + font_awesome_table["door-closed"] = 0xf52a; + font_awesome_table["door-open"] = 0xf52b; + font_awesome_table["dot-circle"] = 0xf192; + font_awesome_table["dove"] = 0xf4ba; + font_awesome_table["download"] = 0xf019; + font_awesome_table["draft2digital"] = 0xf396; + font_awesome_table["drafting-compass"] = 0xf568; + font_awesome_table["draw-polygon"] = 0xf5ee; + font_awesome_table["dribbble"] = 0xf17d; + font_awesome_table["dribbble-square"] = 0xf397; + font_awesome_table["dropbox"] = 0xf16b; + font_awesome_table["drum"] = 0xf569; + font_awesome_table["drum-steelpan"] = 0xf56a; + font_awesome_table["drupal"] = 0xf1a9; + font_awesome_table["dumbbell"] = 0xf44b; + font_awesome_table["dyalog"] = 0xf399; + font_awesome_table["earlybirds"] = 0xf39a; + font_awesome_table["ebay"] = 0xf4f4; + font_awesome_table["edge"] = 0xf282; + font_awesome_table["edit"] = 0xf044; + font_awesome_table["eject"] = 0xf052; + font_awesome_table["elementor"] = 0xf430; + font_awesome_table["ellipsis-h"] = 0xf141; + font_awesome_table["ellipsis-v"] = 0xf142; + font_awesome_table["ello"] = 0xf5f1; + font_awesome_table["ember"] = 0xf423; + font_awesome_table["empire"] = 0xf1d1; + font_awesome_table["envelope"] = 0xf0e0; + font_awesome_table["envelope-open"] = 0xf2b6; + font_awesome_table["envelope-square"] = 0xf199; + font_awesome_table["envira"] = 0xf299; + font_awesome_table["equals"] = 0xf52c; + font_awesome_table["eraser"] = 0xf12d; + font_awesome_table["erlang"] = 0xf39d; + font_awesome_table["ethereum"] = 0xf42e; + font_awesome_table["etsy"] = 0xf2d7; + font_awesome_table["euro-sign"] = 0xf153; + font_awesome_table["exchange-alt"] = 0xf362; + font_awesome_table["exclamation"] = 0xf12a; + font_awesome_table["exclamation-circle"] = 0xf06a; + font_awesome_table["exclamation-triangle"] = 0xf071; + font_awesome_table["expand"] = 0xf065; + font_awesome_table["expand-arrows-alt"] = 0xf31e; + font_awesome_table["expeditedssl"] = 0xf23e; + font_awesome_table["external-link-alt"] = 0xf35d; + font_awesome_table["external-link-square-alt"] = 0xf360; + font_awesome_table["eye"] = 0xf06e; + font_awesome_table["eye-dropper"] = 0xf1fb; + font_awesome_table["eye-slash"] = 0xf070; + font_awesome_table["facebook"] = 0xf09a; + font_awesome_table["facebook-f"] = 0xf39e; + font_awesome_table["facebook-messenger"] = 0xf39f; + font_awesome_table["facebook-square"] = 0xf082; + font_awesome_table["fast-backward"] = 0xf049; + font_awesome_table["fast-forward"] = 0xf050; + font_awesome_table["fax"] = 0xf1ac; + font_awesome_table["feather"] = 0xf52d; + font_awesome_table["feather-alt"] = 0xf56b; + font_awesome_table["female"] = 0xf182; + font_awesome_table["fighter-jet"] = 0xf0fb; + font_awesome_table["file"] = 0xf15b; + font_awesome_table["file-alt"] = 0xf15c; + font_awesome_table["file-archive"] = 0xf1c6; + font_awesome_table["file-audio"] = 0xf1c7; + font_awesome_table["file-code"] = 0xf1c9; + font_awesome_table["file-contract"] = 0xf56c; + font_awesome_table["file-download"] = 0xf56d; + font_awesome_table["file-excel"] = 0xf1c3; + font_awesome_table["file-export"] = 0xf56e; + font_awesome_table["file-image"] = 0xf1c5; + font_awesome_table["file-import"] = 0xf56f; + font_awesome_table["file-invoice"] = 0xf570; + font_awesome_table["file-invoice-dollar"] = 0xf571; + font_awesome_table["file-medical"] = 0xf477; + font_awesome_table["file-medical-alt"] = 0xf478; + font_awesome_table["file-pdf"] = 0xf1c1; + font_awesome_table["file-powerpoint"] = 0xf1c4; + font_awesome_table["file-prescription"] = 0xf572; + font_awesome_table["file-signature"] = 0xf573; + font_awesome_table["file-upload"] = 0xf574; + font_awesome_table["file-video"] = 0xf1c8; + font_awesome_table["file-word"] = 0xf1c2; + font_awesome_table["fill"] = 0xf575; + font_awesome_table["fill-drip"] = 0xf576; + font_awesome_table["film"] = 0xf008; + font_awesome_table["filter"] = 0xf0b0; + font_awesome_table["fingerprint"] = 0xf577; + font_awesome_table["fire"] = 0xf06d; + font_awesome_table["fire-extinguisher"] = 0xf134; + font_awesome_table["firefox"] = 0xf269; + font_awesome_table["first-aid"] = 0xf479; + font_awesome_table["first-order"] = 0xf2b0; + font_awesome_table["first-order-alt"] = 0xf50a; + font_awesome_table["firstdraft"] = 0xf3a1; + font_awesome_table["fish"] = 0xf578; + font_awesome_table["flag"] = 0xf024; + font_awesome_table["flag-checkered"] = 0xf11e; + font_awesome_table["flask"] = 0xf0c3; + font_awesome_table["flickr"] = 0xf16e; + font_awesome_table["flipboard"] = 0xf44d; + font_awesome_table["flushed"] = 0xf579; + font_awesome_table["fly"] = 0xf417; + font_awesome_table["folder"] = 0xf07b; + font_awesome_table["folder-open"] = 0xf07c; + font_awesome_table["font"] = 0xf031; + font_awesome_table["font-awesome"] = 0xf2b4; + font_awesome_table["font-awesome-alt"] = 0xf35c; + font_awesome_table["font-awesome-flag"] = 0xf425; + font_awesome_table["font-awesome-logo-full"] = 0xf4e6; + font_awesome_table["fonticons"] = 0xf280; + font_awesome_table["fonticons-fi"] = 0xf3a2; + font_awesome_table["football-ball"] = 0xf44e; + font_awesome_table["fort-awesome"] = 0xf286; + font_awesome_table["fort-awesome-alt"] = 0xf3a3; + font_awesome_table["forumbee"] = 0xf211; + font_awesome_table["forward"] = 0xf04e; + font_awesome_table["foursquare"] = 0xf180; + font_awesome_table["free-code-camp"] = 0xf2c5; + font_awesome_table["freebsd"] = 0xf3a4; + font_awesome_table["frog"] = 0xf52e; + font_awesome_table["frown"] = 0xf119; + font_awesome_table["frown-open"] = 0xf57a; + font_awesome_table["fulcrum"] = 0xf50b; + font_awesome_table["futbol"] = 0xf1e3; + font_awesome_table["galactic-republic"] = 0xf50c; + font_awesome_table["galactic-senate"] = 0xf50d; + font_awesome_table["gamepad"] = 0xf11b; + font_awesome_table["gas-pump"] = 0xf52f; + font_awesome_table["gavel"] = 0xf0e3; + font_awesome_table["gem"] = 0xf3a5; + font_awesome_table["genderless"] = 0xf22d; + font_awesome_table["get-pocket"] = 0xf265; + font_awesome_table["gg"] = 0xf260; + font_awesome_table["gg-circle"] = 0xf261; + font_awesome_table["gift"] = 0xf06b; + font_awesome_table["git"] = 0xf1d3; + font_awesome_table["git-square"] = 0xf1d2; + font_awesome_table["github"] = 0xf09b; + font_awesome_table["github-alt"] = 0xf113; + font_awesome_table["github-square"] = 0xf092; + font_awesome_table["gitkraken"] = 0xf3a6; + font_awesome_table["gitlab"] = 0xf296; + font_awesome_table["gitter"] = 0xf426; + font_awesome_table["glass-martini"] = 0xf000; + font_awesome_table["glass-martini-alt"] = 0xf57b; + font_awesome_table["glasses"] = 0xf530; + font_awesome_table["glide"] = 0xf2a5; + font_awesome_table["glide-g"] = 0xf2a6; + font_awesome_table["globe"] = 0xf0ac; + font_awesome_table["globe-africa"] = 0xf57c; + font_awesome_table["globe-americas"] = 0xf57d; + font_awesome_table["globe-asia"] = 0xf57e; + font_awesome_table["gofore"] = 0xf3a7; + font_awesome_table["golf-ball"] = 0xf450; + font_awesome_table["goodreads"] = 0xf3a8; + font_awesome_table["goodreads-g"] = 0xf3a9; + font_awesome_table["google"] = 0xf1a0; + font_awesome_table["google-drive"] = 0xf3aa; + font_awesome_table["google-play"] = 0xf3ab; + font_awesome_table["google-plus"] = 0xf2b3; + font_awesome_table["google-plus-g"] = 0xf0d5; + font_awesome_table["google-plus-square"] = 0xf0d4; + font_awesome_table["google-wallet"] = 0xf1ee; + font_awesome_table["graduation-cap"] = 0xf19d; + font_awesome_table["gratipay"] = 0xf184; + font_awesome_table["grav"] = 0xf2d6; + font_awesome_table["greater-than"] = 0xf531; + font_awesome_table["greater-than-equal"] = 0xf532; + font_awesome_table["grimace"] = 0xf57f; + font_awesome_table["grin"] = 0xf580; + font_awesome_table["grin-alt"] = 0xf581; + font_awesome_table["grin-beam"] = 0xf582; + font_awesome_table["grin-beam-sweat"] = 0xf583; + font_awesome_table["grin-hearts"] = 0xf584; + font_awesome_table["grin-squint"] = 0xf585; + font_awesome_table["grin-squint-tears"] = 0xf586; + font_awesome_table["grin-stars"] = 0xf587; + font_awesome_table["grin-tears"] = 0xf588; + font_awesome_table["grin-tongue"] = 0xf589; + font_awesome_table["grin-tongue-squint"] = 0xf58a; + font_awesome_table["grin-tongue-wink"] = 0xf58b; + font_awesome_table["grin-wink"] = 0xf58c; + font_awesome_table["grip-horizontal"] = 0xf58d; + font_awesome_table["grip-vertical"] = 0xf58e; + font_awesome_table["gripfire"] = 0xf3ac; + font_awesome_table["grunt"] = 0xf3ad; + font_awesome_table["gulp"] = 0xf3ae; + font_awesome_table["h-square"] = 0xf0fd; + font_awesome_table["hacker-news"] = 0xf1d4; + font_awesome_table["hacker-news-square"] = 0xf3af; + font_awesome_table["hackerrank"] = 0xf5f7; + font_awesome_table["hand-holding"] = 0xf4bd; + font_awesome_table["hand-holding-heart"] = 0xf4be; + font_awesome_table["hand-holding-usd"] = 0xf4c0; + font_awesome_table["hand-lizard"] = 0xf258; + font_awesome_table["hand-paper"] = 0xf256; + font_awesome_table["hand-peace"] = 0xf25b; + font_awesome_table["hand-point-down"] = 0xf0a7; + font_awesome_table["hand-point-left"] = 0xf0a5; + font_awesome_table["hand-point-right"] = 0xf0a4; + font_awesome_table["hand-point-up"] = 0xf0a6; + font_awesome_table["hand-pointer"] = 0xf25a; + font_awesome_table["hand-rock"] = 0xf255; + font_awesome_table["hand-scissors"] = 0xf257; + font_awesome_table["hand-spock"] = 0xf259; + font_awesome_table["hands"] = 0xf4c2; + font_awesome_table["hands-helping"] = 0xf4c4; + font_awesome_table["handshake"] = 0xf2b5; + font_awesome_table["hashtag"] = 0xf292; + font_awesome_table["hdd"] = 0xf0a0; + font_awesome_table["heading"] = 0xf1dc; + font_awesome_table["headphones"] = 0xf025; + font_awesome_table["headphones-alt"] = 0xf58f; + font_awesome_table["headset"] = 0xf590; + font_awesome_table["heart"] = 0xf004; + font_awesome_table["heartbeat"] = 0xf21e; + font_awesome_table["helicopter"] = 0xf533; + font_awesome_table["highlighter"] = 0xf591; + font_awesome_table["hips"] = 0xf452; + font_awesome_table["hire-a-helper"] = 0xf3b0; + font_awesome_table["history"] = 0xf1da; + font_awesome_table["hockey-puck"] = 0xf453; + font_awesome_table["home"] = 0xf015; + font_awesome_table["hooli"] = 0xf427; + font_awesome_table["hornbill"] = 0xf592; + font_awesome_table["hospital"] = 0xf0f8; + font_awesome_table["hospital-alt"] = 0xf47d; + font_awesome_table["hospital-symbol"] = 0xf47e; + font_awesome_table["hot-tub"] = 0xf593; + font_awesome_table["hotel"] = 0xf594; + font_awesome_table["hotjar"] = 0xf3b1; + font_awesome_table["hourglass"] = 0xf254; + font_awesome_table["hourglass-end"] = 0xf253; + font_awesome_table["hourglass-half"] = 0xf252; + font_awesome_table["hourglass-start"] = 0xf251; + font_awesome_table["houzz"] = 0xf27c; + font_awesome_table["html5"] = 0xf13b; + font_awesome_table["hubspot"] = 0xf3b2; + font_awesome_table["i-cursor"] = 0xf246; + font_awesome_table["id-badge"] = 0xf2c1; + font_awesome_table["id-card"] = 0xf2c2; + font_awesome_table["id-card-alt"] = 0xf47f; + font_awesome_table["image"] = 0xf03e; + font_awesome_table["images"] = 0xf302; + font_awesome_table["imdb"] = 0xf2d8; + font_awesome_table["inbox"] = 0xf01c; + font_awesome_table["indent"] = 0xf03c; + font_awesome_table["industry"] = 0xf275; + font_awesome_table["infinity"] = 0xf534; + font_awesome_table["info"] = 0xf129; + font_awesome_table["info-circle"] = 0xf05a; + font_awesome_table["instagram"] = 0xf16d; + font_awesome_table["internet-explorer"] = 0xf26b; + font_awesome_table["ioxhost"] = 0xf208; + font_awesome_table["italic"] = 0xf033; + font_awesome_table["itunes"] = 0xf3b4; + font_awesome_table["itunes-note"] = 0xf3b5; + font_awesome_table["java"] = 0xf4e4; + font_awesome_table["jedi-order"] = 0xf50e; + font_awesome_table["jenkins"] = 0xf3b6; + font_awesome_table["joget"] = 0xf3b7; + font_awesome_table["joint"] = 0xf595; + font_awesome_table["joomla"] = 0xf1aa; + font_awesome_table["js"] = 0xf3b8; + font_awesome_table["js-square"] = 0xf3b9; + font_awesome_table["jsfiddle"] = 0xf1cc; + font_awesome_table["kaggle"] = 0xf5fa; + font_awesome_table["key"] = 0xf084; + font_awesome_table["keybase"] = 0xf4f5; + font_awesome_table["keyboard"] = 0xf11c; + font_awesome_table["keycdn"] = 0xf3ba; + font_awesome_table["kickstarter"] = 0xf3bb; + font_awesome_table["kickstarter-k"] = 0xf3bc; + font_awesome_table["kiss"] = 0xf596; + font_awesome_table["kiss-beam"] = 0xf597; + font_awesome_table["kiss-wink-heart"] = 0xf598; + font_awesome_table["kiwi-bird"] = 0xf535; + font_awesome_table["korvue"] = 0xf42f; + font_awesome_table["language"] = 0xf1ab; + font_awesome_table["laptop"] = 0xf109; + font_awesome_table["laptop-code"] = 0xf5fc; + font_awesome_table["laravel"] = 0xf3bd; + font_awesome_table["lastfm"] = 0xf202; + font_awesome_table["lastfm-square"] = 0xf203; + font_awesome_table["laugh"] = 0xf599; + font_awesome_table["laugh-beam"] = 0xf59a; + font_awesome_table["laugh-squint"] = 0xf59b; + font_awesome_table["laugh-wink"] = 0xf59c; + font_awesome_table["layer-group"] = 0xf5fd; + font_awesome_table["leaf"] = 0xf06c; + font_awesome_table["leanpub"] = 0xf212; + font_awesome_table["lemon"] = 0xf094; + font_awesome_table["less"] = 0xf41d; + font_awesome_table["less-than"] = 0xf536; + font_awesome_table["less-than-equal"] = 0xf537; + font_awesome_table["level-down-alt"] = 0xf3be; + font_awesome_table["level-up-alt"] = 0xf3bf; + font_awesome_table["life-ring"] = 0xf1cd; + font_awesome_table["lightbulb"] = 0xf0eb; + font_awesome_table["line"] = 0xf3c0; + font_awesome_table["link"] = 0xf0c1; + font_awesome_table["linkedin"] = 0xf08c; + font_awesome_table["linkedin-in"] = 0xf0e1; + font_awesome_table["linode"] = 0xf2b8; + font_awesome_table["linux"] = 0xf17c; + font_awesome_table["lira-sign"] = 0xf195; + font_awesome_table["list"] = 0xf03a; + font_awesome_table["list-alt"] = 0xf022; + font_awesome_table["list-ol"] = 0xf0cb; + font_awesome_table["list-ul"] = 0xf0ca; + font_awesome_table["location-arrow"] = 0xf124; + font_awesome_table["lock"] = 0xf023; + font_awesome_table["lock-open"] = 0xf3c1; + font_awesome_table["long-arrow-alt-down"] = 0xf309; + font_awesome_table["long-arrow-alt-left"] = 0xf30a; + font_awesome_table["long-arrow-alt-right"] = 0xf30b; + font_awesome_table["long-arrow-alt-up"] = 0xf30c; + font_awesome_table["low-vision"] = 0xf2a8; + font_awesome_table["luggage-cart"] = 0xf59d; + font_awesome_table["lyft"] = 0xf3c3; + font_awesome_table["magento"] = 0xf3c4; + font_awesome_table["magic"] = 0xf0d0; + font_awesome_table["magnet"] = 0xf076; + font_awesome_table["mailchimp"] = 0xf59e; + font_awesome_table["male"] = 0xf183; + font_awesome_table["mandalorian"] = 0xf50f; + font_awesome_table["map"] = 0xf279; + font_awesome_table["map-marked"] = 0xf59f; + font_awesome_table["map-marked-alt"] = 0xf5a0; + font_awesome_table["map-marker"] = 0xf041; + font_awesome_table["map-marker-alt"] = 0xf3c5; + font_awesome_table["map-pin"] = 0xf276; + font_awesome_table["map-signs"] = 0xf277; + font_awesome_table["markdown"] = 0xf60f; + font_awesome_table["marker"] = 0xf5a1; + font_awesome_table["mars"] = 0xf222; + font_awesome_table["mars-double"] = 0xf227; + font_awesome_table["mars-stroke"] = 0xf229; + font_awesome_table["mars-stroke-h"] = 0xf22b; + font_awesome_table["mars-stroke-v"] = 0xf22a; + font_awesome_table["mastodon"] = 0xf4f6; + font_awesome_table["maxcdn"] = 0xf136; + font_awesome_table["medal"] = 0xf5a2; + font_awesome_table["medapps"] = 0xf3c6; + font_awesome_table["medium"] = 0xf23a; + font_awesome_table["medium-m"] = 0xf3c7; + font_awesome_table["medkit"] = 0xf0fa; + font_awesome_table["medrt"] = 0xf3c8; + font_awesome_table["meetup"] = 0xf2e0; + font_awesome_table["megaport"] = 0xf5a3; + font_awesome_table["meh"] = 0xf11a; + font_awesome_table["meh-blank"] = 0xf5a4; + font_awesome_table["meh-rolling-eyes"] = 0xf5a5; + font_awesome_table["memory"] = 0xf538; + font_awesome_table["mercury"] = 0xf223; + font_awesome_table["microchip"] = 0xf2db; + font_awesome_table["microphone"] = 0xf130; + font_awesome_table["microphone-alt"] = 0xf3c9; + font_awesome_table["microphone-alt-slash"] = 0xf539; + font_awesome_table["microphone-slash"] = 0xf131; + font_awesome_table["microscope"] = 0xf610; + font_awesome_table["microsoft"] = 0xf3ca; + font_awesome_table["minus"] = 0xf068; + font_awesome_table["minus-circle"] = 0xf056; + font_awesome_table["minus-square"] = 0xf146; + font_awesome_table["mix"] = 0xf3cb; + font_awesome_table["mixcloud"] = 0xf289; + font_awesome_table["mizuni"] = 0xf3cc; + font_awesome_table["mobile"] = 0xf10b; + font_awesome_table["mobile-alt"] = 0xf3cd; + font_awesome_table["modx"] = 0xf285; + font_awesome_table["monero"] = 0xf3d0; + font_awesome_table["money-bill"] = 0xf0d6; + font_awesome_table["money-bill-alt"] = 0xf3d1; + font_awesome_table["money-bill-wave"] = 0xf53a; + font_awesome_table["money-bill-wave-alt"] = 0xf53b; + font_awesome_table["money-check"] = 0xf53c; + font_awesome_table["money-check-alt"] = 0xf53d; + font_awesome_table["monument"] = 0xf5a6; + font_awesome_table["moon"] = 0xf186; + font_awesome_table["mortar-pestle"] = 0xf5a7; + font_awesome_table["motorcycle"] = 0xf21c; + font_awesome_table["mouse-pointer"] = 0xf245; + font_awesome_table["music"] = 0xf001; + font_awesome_table["napster"] = 0xf3d2; + font_awesome_table["neos"] = 0xf612; + font_awesome_table["neuter"] = 0xf22c; + font_awesome_table["newspaper"] = 0xf1ea; + font_awesome_table["nimblr"] = 0xf5a8; + font_awesome_table["nintendo-switch"] = 0xf418; + font_awesome_table["node"] = 0xf419; + font_awesome_table["node-js"] = 0xf3d3; + font_awesome_table["not-equal"] = 0xf53e; + font_awesome_table["notes-medical"] = 0xf481; + font_awesome_table["npm"] = 0xf3d4; + font_awesome_table["ns8"] = 0xf3d5; + font_awesome_table["nutritionix"] = 0xf3d6; + font_awesome_table["object-group"] = 0xf247; + font_awesome_table["object-ungroup"] = 0xf248; + font_awesome_table["odnoklassniki"] = 0xf263; + font_awesome_table["odnoklassniki-square"] = 0xf264; + font_awesome_table["oil-can"] = 0xf613; + font_awesome_table["old-republic"] = 0xf510; + font_awesome_table["opencart"] = 0xf23d; + font_awesome_table["openid"] = 0xf19b; + font_awesome_table["opera"] = 0xf26a; + font_awesome_table["optin-monster"] = 0xf23c; + font_awesome_table["osi"] = 0xf41a; + font_awesome_table["outdent"] = 0xf03b; + font_awesome_table["page4"] = 0xf3d7; + font_awesome_table["pagelines"] = 0xf18c; + font_awesome_table["paint-brush"] = 0xf1fc; + font_awesome_table["paint-roller"] = 0xf5aa; + font_awesome_table["palette"] = 0xf53f; + font_awesome_table["palfed"] = 0xf3d8; + font_awesome_table["pallet"] = 0xf482; + font_awesome_table["paper-plane"] = 0xf1d8; + font_awesome_table["paperclip"] = 0xf0c6; + font_awesome_table["parachute-box"] = 0xf4cd; + font_awesome_table["paragraph"] = 0xf1dd; + font_awesome_table["parking"] = 0xf540; + font_awesome_table["passport"] = 0xf5ab; + font_awesome_table["paste"] = 0xf0ea; + font_awesome_table["patreon"] = 0xf3d9; + font_awesome_table["pause"] = 0xf04c; + font_awesome_table["pause-circle"] = 0xf28b; + font_awesome_table["paw"] = 0xf1b0; + font_awesome_table["paypal"] = 0xf1ed; + font_awesome_table["pen"] = 0xf304; + font_awesome_table["pen-alt"] = 0xf305; + font_awesome_table["pen-fancy"] = 0xf5ac; + font_awesome_table["pen-nib"] = 0xf5ad; + font_awesome_table["pen-square"] = 0xf14b; + font_awesome_table["pencil-alt"] = 0xf303; + font_awesome_table["pencil-ruler"] = 0xf5ae; + font_awesome_table["people-carry"] = 0xf4ce; + font_awesome_table["percent"] = 0xf295; + font_awesome_table["percentage"] = 0xf541; + font_awesome_table["periscope"] = 0xf3da; + font_awesome_table["phabricator"] = 0xf3db; + font_awesome_table["phoenix-framework"] = 0xf3dc; + font_awesome_table["phoenix-squadron"] = 0xf511; + font_awesome_table["phone"] = 0xf095; + font_awesome_table["phone-slash"] = 0xf3dd; + font_awesome_table["phone-square"] = 0xf098; + font_awesome_table["phone-volume"] = 0xf2a0; + font_awesome_table["php"] = 0xf457; + font_awesome_table["pied-piper"] = 0xf2ae; + font_awesome_table["pied-piper-alt"] = 0xf1a8; + font_awesome_table["pied-piper-hat"] = 0xf4e5; + font_awesome_table["pied-piper-pp"] = 0xf1a7; + font_awesome_table["piggy-bank"] = 0xf4d3; + font_awesome_table["pills"] = 0xf484; + font_awesome_table["pinterest"] = 0xf0d2; + font_awesome_table["pinterest-p"] = 0xf231; + font_awesome_table["pinterest-square"] = 0xf0d3; + font_awesome_table["plane"] = 0xf072; + font_awesome_table["plane-arrival"] = 0xf5af; + font_awesome_table["plane-departure"] = 0xf5b0; + font_awesome_table["play"] = 0xf04b; + font_awesome_table["play-circle"] = 0xf144; + font_awesome_table["playstation"] = 0xf3df; + font_awesome_table["plug"] = 0xf1e6; + font_awesome_table["plus"] = 0xf067; + font_awesome_table["plus-circle"] = 0xf055; + font_awesome_table["plus-square"] = 0xf0fe; + font_awesome_table["podcast"] = 0xf2ce; + font_awesome_table["poo"] = 0xf2fe; + font_awesome_table["poop"] = 0xf619; + font_awesome_table["portrait"] = 0xf3e0; + font_awesome_table["pound-sign"] = 0xf154; + font_awesome_table["power-off"] = 0xf011; + font_awesome_table["prescription"] = 0xf5b1; + font_awesome_table["prescription-bottle"] = 0xf485; + font_awesome_table["prescription-bottle-alt"] = 0xf486; + font_awesome_table["print"] = 0xf02f; + font_awesome_table["procedures"] = 0xf487; + font_awesome_table["product-hunt"] = 0xf288; + font_awesome_table["project-diagram"] = 0xf542; + font_awesome_table["pushed"] = 0xf3e1; + font_awesome_table["puzzle-piece"] = 0xf12e; + font_awesome_table["python"] = 0xf3e2; + font_awesome_table["qq"] = 0xf1d6; + font_awesome_table["qrcode"] = 0xf029; + font_awesome_table["question"] = 0xf128; + font_awesome_table["question-circle"] = 0xf059; + font_awesome_table["quidditch"] = 0xf458; + font_awesome_table["quinscape"] = 0xf459; + font_awesome_table["quora"] = 0xf2c4; + font_awesome_table["quote-left"] = 0xf10d; + font_awesome_table["quote-right"] = 0xf10e; + font_awesome_table["r-project"] = 0xf4f7; + font_awesome_table["random"] = 0xf074; + font_awesome_table["ravelry"] = 0xf2d9; + font_awesome_table["react"] = 0xf41b; + font_awesome_table["readme"] = 0xf4d5; + font_awesome_table["rebel"] = 0xf1d0; + font_awesome_table["receipt"] = 0xf543; + font_awesome_table["recycle"] = 0xf1b8; + font_awesome_table["red-river"] = 0xf3e3; + font_awesome_table["reddit"] = 0xf1a1; + font_awesome_table["reddit-alien"] = 0xf281; + font_awesome_table["reddit-square"] = 0xf1a2; + font_awesome_table["redo"] = 0xf01e; + font_awesome_table["redo-alt"] = 0xf2f9; + font_awesome_table["registered"] = 0xf25d; + font_awesome_table["rendact"] = 0xf3e4; + font_awesome_table["renren"] = 0xf18b; + font_awesome_table["reply"] = 0xf3e5; + font_awesome_table["reply-all"] = 0xf122; + font_awesome_table["replyd"] = 0xf3e6; + font_awesome_table["researchgate"] = 0xf4f8; + font_awesome_table["resolving"] = 0xf3e7; + font_awesome_table["retweet"] = 0xf079; + font_awesome_table["rev"] = 0xf5b2; + font_awesome_table["ribbon"] = 0xf4d6; + font_awesome_table["road"] = 0xf018; + font_awesome_table["robot"] = 0xf544; + font_awesome_table["rocket"] = 0xf135; + font_awesome_table["rocketchat"] = 0xf3e8; + font_awesome_table["rockrms"] = 0xf3e9; + font_awesome_table["route"] = 0xf4d7; + font_awesome_table["rss"] = 0xf09e; + font_awesome_table["rss-square"] = 0xf143; + font_awesome_table["ruble-sign"] = 0xf158; + font_awesome_table["ruler"] = 0xf545; + font_awesome_table["ruler-combined"] = 0xf546; + font_awesome_table["ruler-horizontal"] = 0xf547; + font_awesome_table["ruler-vertical"] = 0xf548; + font_awesome_table["rupee-sign"] = 0xf156; + font_awesome_table["sad-cry"] = 0xf5b3; + font_awesome_table["sad-tear"] = 0xf5b4; + font_awesome_table["safari"] = 0xf267; + font_awesome_table["sass"] = 0xf41e; + font_awesome_table["save"] = 0xf0c7; + font_awesome_table["schlix"] = 0xf3ea; + font_awesome_table["school"] = 0xf549; + font_awesome_table["screwdriver"] = 0xf54a; + font_awesome_table["scribd"] = 0xf28a; + font_awesome_table["search"] = 0xf002; + font_awesome_table["search-minus"] = 0xf010; + font_awesome_table["search-plus"] = 0xf00e; + font_awesome_table["searchengin"] = 0xf3eb; + font_awesome_table["seedling"] = 0xf4d8; + font_awesome_table["sellcast"] = 0xf2da; + font_awesome_table["sellsy"] = 0xf213; + font_awesome_table["server"] = 0xf233; + font_awesome_table["servicestack"] = 0xf3ec; + font_awesome_table["shapes"] = 0xf61f; + font_awesome_table["share"] = 0xf064; + font_awesome_table["share-alt"] = 0xf1e0; + font_awesome_table["share-alt-square"] = 0xf1e1; + font_awesome_table["share-square"] = 0xf14d; + font_awesome_table["shekel-sign"] = 0xf20b; + font_awesome_table["shield-alt"] = 0xf3ed; + font_awesome_table["ship"] = 0xf21a; + font_awesome_table["shipping-fast"] = 0xf48b; + font_awesome_table["shirtsinbulk"] = 0xf214; + font_awesome_table["shoe-prints"] = 0xf54b; + font_awesome_table["shopping-bag"] = 0xf290; + font_awesome_table["shopping-basket"] = 0xf291; + font_awesome_table["shopping-cart"] = 0xf07a; + font_awesome_table["shopware"] = 0xf5b5; + font_awesome_table["shower"] = 0xf2cc; + font_awesome_table["shuttle-van"] = 0xf5b6; + font_awesome_table["sign"] = 0xf4d9; + font_awesome_table["sign-in-alt"] = 0xf2f6; + font_awesome_table["sign-language"] = 0xf2a7; + font_awesome_table["sign-out-alt"] = 0xf2f5; + font_awesome_table["signal"] = 0xf012; + font_awesome_table["signature"] = 0xf5b7; + font_awesome_table["simplybuilt"] = 0xf215; + font_awesome_table["sistrix"] = 0xf3ee; + font_awesome_table["sitemap"] = 0xf0e8; + font_awesome_table["sith"] = 0xf512; + font_awesome_table["skull"] = 0xf54c; + font_awesome_table["skyatlas"] = 0xf216; + font_awesome_table["skype"] = 0xf17e; + font_awesome_table["slack"] = 0xf198; + font_awesome_table["slack-hash"] = 0xf3ef; + font_awesome_table["sliders-h"] = 0xf1de; + font_awesome_table["slideshare"] = 0xf1e7; + font_awesome_table["smile"] = 0xf118; + font_awesome_table["smile-beam"] = 0xf5b8; + font_awesome_table["smile-wink"] = 0xf4da; + font_awesome_table["smoking"] = 0xf48d; + font_awesome_table["smoking-ban"] = 0xf54d; + font_awesome_table["snapchat"] = 0xf2ab; + font_awesome_table["snapchat-ghost"] = 0xf2ac; + font_awesome_table["snapchat-square"] = 0xf2ad; + font_awesome_table["snowflake"] = 0xf2dc; + font_awesome_table["solar-panel"] = 0xf5ba; + font_awesome_table["sort"] = 0xf0dc; + font_awesome_table["sort-alpha-down"] = 0xf15d; + font_awesome_table["sort-alpha-up"] = 0xf15e; + font_awesome_table["sort-amount-down"] = 0xf160; + font_awesome_table["sort-amount-up"] = 0xf161; + font_awesome_table["sort-down"] = 0xf0dd; + font_awesome_table["sort-numeric-down"] = 0xf162; + font_awesome_table["sort-numeric-up"] = 0xf163; + font_awesome_table["sort-up"] = 0xf0de; + font_awesome_table["soundcloud"] = 0xf1be; + font_awesome_table["spa"] = 0xf5bb; + font_awesome_table["space-shuttle"] = 0xf197; + font_awesome_table["speakap"] = 0xf3f3; + font_awesome_table["spinner"] = 0xf110; + font_awesome_table["splotch"] = 0xf5bc; + font_awesome_table["spotify"] = 0xf1bc; + font_awesome_table["spray-can"] = 0xf5bd; + font_awesome_table["square"] = 0xf0c8; + font_awesome_table["square-full"] = 0xf45c; + font_awesome_table["squarespace"] = 0xf5be; + font_awesome_table["stack-exchange"] = 0xf18d; + font_awesome_table["stack-overflow"] = 0xf16c; + font_awesome_table["stamp"] = 0xf5bf; + font_awesome_table["star"] = 0xf005; + font_awesome_table["star-half"] = 0xf089; + font_awesome_table["star-half-alt"] = 0xf5c0; + font_awesome_table["star-of-life"] = 0xf621; + font_awesome_table["staylinked"] = 0xf3f5; + font_awesome_table["steam"] = 0xf1b6; + font_awesome_table["steam-square"] = 0xf1b7; + font_awesome_table["steam-symbol"] = 0xf3f6; + font_awesome_table["step-backward"] = 0xf048; + font_awesome_table["step-forward"] = 0xf051; + font_awesome_table["stethoscope"] = 0xf0f1; + font_awesome_table["sticker-mule"] = 0xf3f7; + font_awesome_table["sticky-note"] = 0xf249; + font_awesome_table["stop"] = 0xf04d; + font_awesome_table["stop-circle"] = 0xf28d; + font_awesome_table["stopwatch"] = 0xf2f2; + font_awesome_table["store"] = 0xf54e; + font_awesome_table["store-alt"] = 0xf54f; + font_awesome_table["strava"] = 0xf428; + font_awesome_table["stream"] = 0xf550; + font_awesome_table["street-view"] = 0xf21d; + font_awesome_table["strikethrough"] = 0xf0cc; + font_awesome_table["stripe"] = 0xf429; + font_awesome_table["stripe-s"] = 0xf42a; + font_awesome_table["stroopwafel"] = 0xf551; + font_awesome_table["studiovinari"] = 0xf3f8; + font_awesome_table["stumbleupon"] = 0xf1a4; + font_awesome_table["stumbleupon-circle"] = 0xf1a3; + font_awesome_table["subscript"] = 0xf12c; + font_awesome_table["subway"] = 0xf239; + font_awesome_table["suitcase"] = 0xf0f2; + font_awesome_table["suitcase-rolling"] = 0xf5c1; + font_awesome_table["sun"] = 0xf185; + font_awesome_table["superpowers"] = 0xf2dd; + font_awesome_table["superscript"] = 0xf12b; + font_awesome_table["supple"] = 0xf3f9; + font_awesome_table["surprise"] = 0xf5c2; + font_awesome_table["swatchbook"] = 0xf5c3; + font_awesome_table["swimmer"] = 0xf5c4; + font_awesome_table["swimming-pool"] = 0xf5c5; + font_awesome_table["sync"] = 0xf021; + font_awesome_table["sync-alt"] = 0xf2f1; + font_awesome_table["syringe"] = 0xf48e; + font_awesome_table["font_awesome_table"] = 0xf0ce; + font_awesome_table["font_awesome_table-tennis"] = 0xf45d; + font_awesome_table["font_awesome_tablet"] = 0xf10a; + font_awesome_table["font_awesome_tablet-alt"] = 0xf3fa; + font_awesome_table["font_awesome_tablets"] = 0xf490; + font_awesome_table["tachometer-alt"] = 0xf3fd; + font_awesome_table["tag"] = 0xf02b; + font_awesome_table["tags"] = 0xf02c; + font_awesome_table["tape"] = 0xf4db; + font_awesome_table["tasks"] = 0xf0ae; + font_awesome_table["taxi"] = 0xf1ba; + font_awesome_table["teamspeak"] = 0xf4f9; + font_awesome_table["teeth"] = 0xf62e; + font_awesome_table["teeth-open"] = 0xf62f; + font_awesome_table["telegram"] = 0xf2c6; + font_awesome_table["telegram-plane"] = 0xf3fe; + font_awesome_table["tencent-weibo"] = 0xf1d5; + font_awesome_table["terminal"] = 0xf120; + font_awesome_table["text-height"] = 0xf034; + font_awesome_table["text-width"] = 0xf035; + font_awesome_table["th"] = 0xf00a; + font_awesome_table["th-large"] = 0xf009; + font_awesome_table["th-list"] = 0xf00b; + font_awesome_table["theater-masks"] = 0xf630; + font_awesome_table["themeco"] = 0xf5c6; + font_awesome_table["themeisle"] = 0xf2b2; + font_awesome_table["thermometer"] = 0xf491; + font_awesome_table["thermometer-empty"] = 0xf2cb; + font_awesome_table["thermometer-full"] = 0xf2c7; + font_awesome_table["thermometer-half"] = 0xf2c9; + font_awesome_table["thermometer-quarter"] = 0xf2ca; + font_awesome_table["thermometer-three-quarters"] = 0xf2c8; + font_awesome_table["thumbs-down"] = 0xf165; + font_awesome_table["thumbs-up"] = 0xf164; + font_awesome_table["thumbtack"] = 0xf08d; + font_awesome_table["ticket-alt"] = 0xf3ff; + font_awesome_table["times"] = 0xf00d; + font_awesome_table["times-circle"] = 0xf057; + font_awesome_table["tint"] = 0xf043; + font_awesome_table["tint-slash"] = 0xf5c7; + font_awesome_table["tired"] = 0xf5c8; + font_awesome_table["toggle-off"] = 0xf204; + font_awesome_table["toggle-on"] = 0xf205; + font_awesome_table["toolbox"] = 0xf552; + font_awesome_table["tooth"] = 0xf5c9; + font_awesome_table["trade-federation"] = 0xf513; + font_awesome_table["trademark"] = 0xf25c; + font_awesome_table["traffic-light"] = 0xf637; + font_awesome_table["train"] = 0xf238; + font_awesome_table["transgender"] = 0xf224; + font_awesome_table["transgender-alt"] = 0xf225; + font_awesome_table["trash"] = 0xf1f8; + font_awesome_table["trash-alt"] = 0xf2ed; + font_awesome_table["tree"] = 0xf1bb; + font_awesome_table["trello"] = 0xf181; + font_awesome_table["tripadvisor"] = 0xf262; + font_awesome_table["trophy"] = 0xf091; + font_awesome_table["truck"] = 0xf0d1; + font_awesome_table["truck-loading"] = 0xf4de; + font_awesome_table["truck-monster"] = 0xf63b; + font_awesome_table["truck-moving"] = 0xf4df; + font_awesome_table["truck-pickup"] = 0xf63c; + font_awesome_table["tshirt"] = 0xf553; + font_awesome_table["tty"] = 0xf1e4; + font_awesome_table["tumblr"] = 0xf173; + font_awesome_table["tumblr-square"] = 0xf174; + font_awesome_table["tv"] = 0xf26c; + font_awesome_table["twitch"] = 0xf1e8; + font_awesome_table["twitter"] = 0xf099; + font_awesome_table["twitter-square"] = 0xf081; + font_awesome_table["typo3"] = 0xf42b; + font_awesome_table["uber"] = 0xf402; + font_awesome_table["uikit"] = 0xf403; + font_awesome_table["umbrella"] = 0xf0e9; + font_awesome_table["umbrella-beach"] = 0xf5ca; + font_awesome_table["underline"] = 0xf0cd; + font_awesome_table["undo"] = 0xf0e2; + font_awesome_table["undo-alt"] = 0xf2ea; + font_awesome_table["uniregistry"] = 0xf404; + font_awesome_table["universal-access"] = 0xf29a; + font_awesome_table["university"] = 0xf19c; + font_awesome_table["unlink"] = 0xf127; + font_awesome_table["unlock"] = 0xf09c; + font_awesome_table["unlock-alt"] = 0xf13e; + font_awesome_table["untappd"] = 0xf405; + font_awesome_table["upload"] = 0xf093; + font_awesome_table["usb"] = 0xf287; + font_awesome_table["user"] = 0xf007; + font_awesome_table["user-alt"] = 0xf406; + font_awesome_table["user-alt-slash"] = 0xf4fa; + font_awesome_table["user-astronaut"] = 0xf4fb; + font_awesome_table["user-check"] = 0xf4fc; + font_awesome_table["user-circle"] = 0xf2bd; + font_awesome_table["user-clock"] = 0xf4fd; + font_awesome_table["user-cog"] = 0xf4fe; + font_awesome_table["user-edit"] = 0xf4ff; + font_awesome_table["user-friends"] = 0xf500; + font_awesome_table["user-graduate"] = 0xf501; + font_awesome_table["user-lock"] = 0xf502; + font_awesome_table["user-md"] = 0xf0f0; + font_awesome_table["user-minus"] = 0xf503; + font_awesome_table["user-ninja"] = 0xf504; + font_awesome_table["user-plus"] = 0xf234; + font_awesome_table["user-secret"] = 0xf21b; + font_awesome_table["user-shield"] = 0xf505; + font_awesome_table["user-slash"] = 0xf506; + font_awesome_table["user-tag"] = 0xf507; + font_awesome_table["user-tie"] = 0xf508; + font_awesome_table["user-times"] = 0xf235; + font_awesome_table["users"] = 0xf0c0; + font_awesome_table["users-cog"] = 0xf509; + font_awesome_table["ussunnah"] = 0xf407; + font_awesome_table["utensil-spoon"] = 0xf2e5; + font_awesome_table["utensils"] = 0xf2e7; + font_awesome_table["vaadin"] = 0xf408; + font_awesome_table["vector-square"] = 0xf5cb; + font_awesome_table["venus"] = 0xf221; + font_awesome_table["venus-double"] = 0xf226; + font_awesome_table["venus-mars"] = 0xf228; + font_awesome_table["viacoin"] = 0xf237; + font_awesome_table["viadeo"] = 0xf2a9; + font_awesome_table["viadeo-square"] = 0xf2aa; + font_awesome_table["vial"] = 0xf492; + font_awesome_table["vials"] = 0xf493; + font_awesome_table["viber"] = 0xf409; + font_awesome_table["video"] = 0xf03d; + font_awesome_table["video-slash"] = 0xf4e2; + font_awesome_table["vimeo"] = 0xf40a; + font_awesome_table["vimeo-square"] = 0xf194; + font_awesome_table["vimeo-v"] = 0xf27d; + font_awesome_table["vine"] = 0xf1ca; + font_awesome_table["vk"] = 0xf189; + font_awesome_table["vnv"] = 0xf40b; + font_awesome_table["volleyball-ball"] = 0xf45f; + font_awesome_table["volume-down"] = 0xf027; + font_awesome_table["volume-off"] = 0xf026; + font_awesome_table["volume-up"] = 0xf028; + font_awesome_table["vuejs"] = 0xf41f; + font_awesome_table["walking"] = 0xf554; + font_awesome_table["wallet"] = 0xf555; + font_awesome_table["warehouse"] = 0xf494; + font_awesome_table["weebly"] = 0xf5cc; + font_awesome_table["weibo"] = 0xf18a; + font_awesome_table["weight"] = 0xf496; + font_awesome_table["weight-hanging"] = 0xf5cd; + font_awesome_table["weixin"] = 0xf1d7; + font_awesome_table["whatsapp"] = 0xf232; + font_awesome_table["whatsapp-square"] = 0xf40c; + font_awesome_table["wheelchair"] = 0xf193; + font_awesome_table["whmcs"] = 0xf40d; + font_awesome_table["wifi"] = 0xf1eb; + font_awesome_table["wikipedia-w"] = 0xf266; + font_awesome_table["window-close"] = 0xf410; + font_awesome_table["window-maximize"] = 0xf2d0; + font_awesome_table["window-minimize"] = 0xf2d1; + font_awesome_table["window-restore"] = 0xf2d2; + font_awesome_table["windows"] = 0xf17a; + font_awesome_table["wine-glass"] = 0xf4e3; + font_awesome_table["wine-glass-alt"] = 0xf5ce; + font_awesome_table["wix"] = 0xf5cf; + font_awesome_table["wolf-pack-battalion"] = 0xf514; + font_awesome_table["won-sign"] = 0xf159; + font_awesome_table["wordpress"] = 0xf19a; + font_awesome_table["wordpress-simple"] = 0xf411; + font_awesome_table["wpbeginner"] = 0xf297; + font_awesome_table["wpexplorer"] = 0xf2de; + font_awesome_table["wpforms"] = 0xf298; + font_awesome_table["wrench"] = 0xf0ad; + font_awesome_table["x-ray"] = 0xf497; + font_awesome_table["xbox"] = 0xf412; + font_awesome_table["xing"] = 0xf168; + font_awesome_table["xing-square"] = 0xf169; + font_awesome_table["y-combinator"] = 0xf23b; + font_awesome_table["yahoo"] = 0xf19e; + font_awesome_table["yandex"] = 0xf413; + font_awesome_table["yandex-international"] = 0xf414; + font_awesome_table["yelp"] = 0xf1e9; + font_awesome_table["yen-sign"] = 0xf157; + font_awesome_table["yoast"] = 0xf2b1; + font_awesome_table["youtube"] = 0xf167; + font_awesome_table["youtube-square"] = 0xf431; + font_awesome_table["zhihu"] = 0xf63f; + } +} + +wchar_t icon_wchar(const char* name) { + auto it = font_awesome_table.find(name); + if(it == font_awesome_table.end()) { + return '\0'; + } + return it->second; +} + +void init_icon_table() { + init_font_awesome_table(); +} diff --git a/src/lib/geogram_gfx/ImGui_ext/icon_font.h b/src/lib/geogram_gfx/ImGui_ext/icon_font.h new file mode 100755 index 00000000..a712e7ef --- /dev/null +++ b/src/lib/geogram_gfx/ImGui_ext/icon_font.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2012-2016, Bruno Levy + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the ALICE Project-Team nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * If you modify this software, you should include a notice giving the + * name of the person performing the modification, the date of modification, + * and the reason for such modification. + * + * Contact: Bruno Levy + * + * Bruno.Levy@inria.fr + * http://www.loria.fr/~levy + * + * ALICE Project + * LORIA, INRIA Lorraine, + * Campus Scientifique, BP 239 + * 54506 VANDOEUVRE LES NANCY CEDEX + * FRANCE + * + */ + +#ifndef GEOGRAM_GFX_IMGUI_EXT_ICON_FONT +#define GEOGRAM_GFX_IMGUI_EXT_ICON_FONT + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file geogram_gfx/ImGui_ext/icon_font.h + * \brief Maps symoblic icon names to 16 bits chars. + */ + +/** + * \brief Gets an icon by name. + * \param[in] name the symbolic name of the icon. + * \return a wchar_t with the icon or '\0' if + * there is no such icon. + */ +wchar_t GEOGRAM_GFX_API icon_wchar(const char* name); + +/** + * \brief Initializes the icon table. + * \details Call this function after merging the font + * with the default ImGui font. + */ +void GEOGRAM_GFX_API init_icon_table(); + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/lib/geogram_gfx/basic/GL.cpp b/src/lib/geogram_gfx/basic/GL.cpp index 02041c85..d49e3d0a 100644 --- a/src/lib/geogram_gfx/basic/GL.cpp +++ b/src/lib/geogram_gfx/basic/GL.cpp @@ -256,18 +256,6 @@ namespace GEO { } } -#ifdef GEO_USE_DEPRECATED_GL - - void glLoadMatrix(const mat4& m) { - glLoadMatrixd(convert_matrix(m)); - } - - void glMultMatrix(const mat4& m) { - glMultMatrixd(convert_matrix(m)); - } - -#endif - void glupMapTexCoords1d(double minval, double maxval, index_t mult) { glupMatrixMode(GLUP_TEXTURE_MATRIX); GLUPdouble M[16]; @@ -469,9 +457,6 @@ namespace GEO { error_code = glGetError() ; } geo_argused(has_opengl_errors); -// if(has_opengl_errors) { -// abort(); -// } } void clear_gl_error_flags(const char* file, int line) { @@ -485,42 +470,6 @@ namespace GEO { } void draw_unit_textured_quad() { -#ifdef GEO_GL_LEGACY - static bool initialized = false; - static bool vanillaGL = false; - if(!initialized) { - vanillaGL = ( - CmdLine::get_arg("gfx:GLUP_profile") == "VanillaGL" - ); - initialized = true; - } - if(vanillaGL) { - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - glDisable(GL_LIGHTING); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glEnable(GL_TEXTURE_2D); - glBegin(GL_QUADS); - glTexCoord2f(0.0f, 0.0f); - glVertex2f(-1.0f, -1.0f); - glTexCoord2f(1.0f, 0.0f); - glVertex2f(1.0f, -1.0f); - glTexCoord2f(1.0f, 1.0f); - glVertex2f(1.0f, 1.0f); - glTexCoord2f(0.0f, 1.0f); - glVertex2f(-1.0f, 1.0f); - glEnd(); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - glDisable(GL_TEXTURE_2D); - return; - } -#endif if(quad_VAO == 0) { create_quad_VAO_and_program(); } diff --git a/src/lib/geogram_gfx/basic/GL.h b/src/lib/geogram_gfx/basic/GL.h index 898f405c..9b859b29 100644 --- a/src/lib/geogram_gfx/basic/GL.h +++ b/src/lib/geogram_gfx/basic/GL.h @@ -74,13 +74,9 @@ # define GEO_GL_ES2 # define GEO_GL_150 # define GEO_GL_NO_DOUBLES -//# define GL_POINT_SPRITE 0x8861 -//# define GL_PROGRAM_POINT_SIZE 0x8642 -//# define GL_CLIP_DISTANCE0 0x3000 #else # include # define GEO_GL_TEXTURE_3D -# define GEO_GL_LEGACY # define GEO_GL_150 # define GEO_GL_440 # define GEO_GL_ES2 @@ -311,7 +307,7 @@ namespace GEO { * \brief Draws a textured quad. * \details The textured quad spans the [-1,1]x[-1,1] square with * texture coordinates in [0,1]x[0,1]. If no program is currently - * bound, then a default one is used, and it uses the texture bind + * bound, then a default one is used, and it uses the texture bound * to unit 0 of GL_TEXTURE_2D. If a program is bound, then it is used. * Vertices coordinates are sent to vertex attribute 0 and texture * coordinates to vertex attribute 1. @@ -330,87 +326,6 @@ namespace GEO { #endif /***********************************************************/ - -#ifdef GEO_USE_DEPRECATED_GL - - /** - * \brief Sends a vertex to OpenGL. - * \param[in] v a const reference to the vertex to be sent. - * \note This uses the old pipeline (glBegin() / glEnd() calls). - * \deprecated use glupVertex(), glupBegin(), glupEnd() instead - */ - inline void glVertex(const vec3& v) { - glVertex3dv(v.data()); - } - - /** - * \brief Sends a vertex to OpenGL. - * \param[in] v a const reference to the vertex to be sent, in - * homogeneous coordinates (4d). - * \note This uses the old pipeline (glBegin() / glEnd() calls). - * \deprecated use glupVertex(), glupBegin(), glupEnd() instead - */ - inline void glVertex(const vec4& v) { - glVertex4dv(v.data()); - } - - /** - * \brief Sends a RGB color to OpenGL. - * \param[in] v a const reference to the color to be sent. - * \note This uses the old pipeline (glBegin() / glEnd() calls). - * \deprecated use glupColor(), glupBegin(), glupEnd() instead - */ - inline void glColor(const vec3& v) { - glColor3dv(v.data()); - } - - /** - * \brief Sends a RGBA color to OpenGL. - * \param[in] v a const reference to the color to be sent. - * \note This uses the old pipeline (glBegin() / glEnd() calls). - * \deprecated use glupColor(), glupBegin(), glupEnd() instead - */ - inline void glColor(const vec4& v) { - glColor4dv(v.data()); - } - - /** - * \brief Sends a normal to OpenGL. - * \param[in] v a const reference to the normal to be sent. - * \note This uses the old pipeline (glBegin() / glEnd() calls). - * \deprecated - */ - inline void glNormal(const vec3& v) { - glNormal3dv(v.data()); - } - - /** - * \brief Multiplies the current OpenGL matrix - * with another one. - * \param[in] m a const reference to the matrix. - * \note m is transposed before being sent to OpenGL - * because Geogram uses the convention with column - * vectors and OpenGL the convention with row vectors - * to represent the transformed points. - * \deprecated use glupMultMatrix() instead. - */ - void GEOGRAM_GFX_API glMultMatrix(const mat4& m); - - /** - * \brief Replaces the current OpenGL matrix - * with a user defined one. - * \param[in] m a const reference to the matrix. - * \note m is transposed before being sent to OpenGL - * because Geogram uses the convention with column - * vectors and OpenGL the convention with row vectors - * to represent the transformed points. - * \deprecated use glupLoadMatrix() instead. - */ - void GEOGRAM_GFX_API glLoadMatrix(const mat4& m); - - /*******************************************************/ - -#endif } #endif diff --git a/src/lib/geogram_gfx/basic/GLSL.cpp b/src/lib/geogram_gfx/basic/GLSL.cpp index af489c0e..402626ad 100644 --- a/src/lib/geogram_gfx/basic/GLSL.cpp +++ b/src/lib/geogram_gfx/basic/GLSL.cpp @@ -334,14 +334,6 @@ namespace GEO { GLSL_version = find_version_number(shading_language_ver_str); Logger::out("GLSL") << "version = " << GLSL_version << std::endl; - - if(!CmdLine::get_arg_bool("gfx:GLSL")) { - Logger::out("GLSL") - << "OpenGL shaders deactivated (gfx:GLSL=false)" - << std::endl; - - GLSL_version = 0.0; - } return GLSL_version; } diff --git a/src/lib/geogram_gfx/glup_viewer/glup_viewer_gui_private.cpp b/src/lib/geogram_gfx/glup_viewer/glup_viewer_gui_private.cpp index 6fe473e3..2672a646 100644 --- a/src/lib/geogram_gfx/glup_viewer/glup_viewer_gui_private.cpp +++ b/src/lib/geogram_gfx/glup_viewer/glup_viewer_gui_private.cpp @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include @@ -60,9 +59,6 @@ #include #include -#ifdef GEO_GL_LEGACY -static bool vanillaGL = false; -#endif /***************************************************************************/ @@ -101,14 +97,6 @@ void glup_viewer_gui_init(GLFWwindow* w) { ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); ImGui_ImplGlfw_InitForOpenGL(w,false); -#ifdef GEO_GL_LEGACY - vanillaGL = (strcmp(glupCurrentProfileName(), "VanillaGL") == 0); - if(vanillaGL) { - GEO::Logger::out("ImGUI") << "Viewer GUI init (Vanilla)" - << std::endl; - ImGui_ImplOpenGL2_Init(); - } else -#endif { GEO::Logger::out("ImGUI") << "Viewer GUI init (GL3)" << std::endl; @@ -147,14 +135,7 @@ void glup_viewer_gui_init(GLFWwindow* w) { } void glup_viewer_gui_cleanup() { -#ifdef GEO_GL_LEGACY - if(vanillaGL) { - ImGui_ImplOpenGL2_Shutdown(); - } else -#endif - { - ImGui_ImplOpenGL3_Shutdown(); - } + ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplGlfw_Shutdown(); ImGui::DestroyContext(); } @@ -162,14 +143,7 @@ void glup_viewer_gui_cleanup() { void glup_viewer_gui_begin_frame() { glup_viewer_gui_locked = true; -#ifdef GEO_GL_LEGACY - if(vanillaGL) { - ImGui_ImplOpenGL2_NewFrame(); - } else -#endif - { - ImGui_ImplOpenGL3_NewFrame(); - } + ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); } @@ -179,14 +153,7 @@ void glup_viewer_gui_end_frame() { if(overlay_func != nullptr) { overlay_func(); ImGui::Render(); -#ifdef GEO_GL_LEGACY - if(vanillaGL) { - ImGui_ImplOpenGL2_RenderDrawData(ImGui::GetDrawData()); - } else -#endif - { - ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); - } + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); } glup_viewer_gui_locked = false; // We flush the queued command here, once ImGui::Render() was diff --git a/src/lib/geogram_gfx/lua/lua_imgui.cpp b/src/lib/geogram_gfx/lua/lua_imgui.cpp index ab503c8e..a8767c00 100644 --- a/src/lib/geogram_gfx/lua/lua_imgui.cpp +++ b/src/lib/geogram_gfx/lua/lua_imgui.cpp @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -56,8 +57,6 @@ extern lua_State* lState; namespace { using namespace GEO; - std::map font_awesome_table; - void init_font_awesome_table(void); int wrapper_TextInput(lua_State* L) { @@ -529,27 +528,55 @@ namespace { } if(!lua_isstring(L,1)) { return luaL_error( - L, "'imgui.font_icon()' argument is not an integer" + L, "'imgui.font_icon()' argument is not a string" ); } const char* K = lua_tostring(L,1); - auto it = font_awesome_table.find(K); wchar_t result[2]; - result[0] = '\0'; + result[0] = icon_wchar(K); result[1] = '\0'; - if(it != font_awesome_table.end()) { - result[0] = it->second; - } std::string result_str = String::wchar_to_UTF8(result); lua_pushstring(L, result_str.c_str()); return 1; } + + int wrapper_BeginTabBar(lua_State* L) { + if(lua_gettop(L) != 1) { + return luaL_error( + L, "'imgui.BeginTabBar()' invalid number of arguments" + ); + } + if(!lua_isstring(L,1)) { + return luaL_error( + L, "'imgui.BeinTabBar()' argument is not a string" + ); + } + const char* K = lua_tostring(L,1); + ImGui::BeginTabBar(K); + return 0; + } + + int wrapper_BeginTabItem(lua_State* L) { + if(lua_gettop(L) != 1) { + return luaL_error( + L, "'imgui.BeginTabItem()' invalid number of arguments" + ); + } + if(!lua_isstring(L,1)) { + return luaL_error( + L, "'imgui.BeginTabItem()' argument is not a string" + ); + } + const char* K = lua_tostring(L,1); + lua_pushboolean(L,ImGui::BeginTabItem(K)); + return 1; + } + } void init_lua_imgui(lua_State* L) { lState = L; LoadImguiBindings(); - init_font_awesome_table(); lua_pushinteger(L, ImGuiExtFileDialogFlags_Load); lua_setglobal(L,"ImGuiExtFileDialogFlags_Load"); @@ -569,9 +596,11 @@ void init_lua_imgui(lua_State* L) { lua_pushinteger(L, ImGuiCond_Appearing); lua_setglobal(L,"ImGuiCond_Appearing"); - lua_pushinteger(L, ImGuiSelectableFlags_AllowDoubleClick); lua_setglobal(L, "ImGuiSelectableFlags_AllowDoubleClick"); + + lua_pushinteger(L, ImGuiCol_Button); + lua_setglobal(L, "ImGuiCol_Button"); lua_getglobal(L, "imgui"); @@ -630,1176 +659,15 @@ void init_lua_imgui(lua_State* L) { lua_pushliteral(L,"font_icon"); lua_pushcfunction(L,wrapper_font_icon); lua_settable(L,-3); - - lua_pop(L,1); -} -namespace { + lua_pushliteral(L,"BeginTabBar"); + lua_pushcfunction(L,wrapper_BeginTabBar); + lua_settable(L,-3); -/* - Table below generated from metadata in FontAwesome distrib: - cat icons.yml | awk 'BEGIN { state = 0; } { - if(state == 0) { - key = $1; - gsub(":","",key); - state = 1; - } else if(state == 1 && $1 == "unicode:") { - printf("font_awesome_table[\"%s\"] = 0x%s;\n",key,$2) - state = 0; - } - }' -*/ - void init_font_awesome_table() { - static bool initialized = false; - if(initialized) { - return; - } - initialized = true; - font_awesome_table["500px"] = 0xf26e; - font_awesome_table["accessible-icon"] = 0xf368; - font_awesome_table["accusoft"] = 0xf369; - font_awesome_table["address-book"] = 0xf2b9; - font_awesome_table["address-card"] = 0xf2bb; - font_awesome_table["adjust"] = 0xf042; - font_awesome_table["adn"] = 0xf170; - font_awesome_table["adversal"] = 0xf36a; - font_awesome_table["affiliatetheme"] = 0xf36b; - font_awesome_table["air-freshener"] = 0xf5d0; - font_awesome_table["algolia"] = 0xf36c; - font_awesome_table["align-center"] = 0xf037; - font_awesome_table["align-justify"] = 0xf039; - font_awesome_table["align-left"] = 0xf036; - font_awesome_table["align-right"] = 0xf038; - font_awesome_table["allergies"] = 0xf461; - font_awesome_table["amazon"] = 0xf270; - font_awesome_table["amazon-pay"] = 0xf42c; - font_awesome_table["ambulance"] = 0xf0f9; - font_awesome_table["american-sign-language-interpreting"] = 0xf2a3; - font_awesome_table["amilia"] = 0xf36d; - font_awesome_table["anchor"] = 0xf13d; - font_awesome_table["android"] = 0xf17b; - font_awesome_table["angellist"] = 0xf209; - font_awesome_table["angle-double-down"] = 0xf103; - font_awesome_table["angle-double-left"] = 0xf100; - font_awesome_table["angle-double-right"] = 0xf101; - font_awesome_table["angle-double-up"] = 0xf102; - font_awesome_table["angle-down"] = 0xf107; - font_awesome_table["angle-left"] = 0xf104; - font_awesome_table["angle-right"] = 0xf105; - font_awesome_table["angle-up"] = 0xf106; - font_awesome_table["angry"] = 0xf556; - font_awesome_table["angrycreative"] = 0xf36e; - font_awesome_table["angular"] = 0xf420; - font_awesome_table["app-store"] = 0xf36f; - font_awesome_table["app-store-ios"] = 0xf370; - font_awesome_table["apper"] = 0xf371; - font_awesome_table["apple"] = 0xf179; - font_awesome_table["apple-alt"] = 0xf5d1; - font_awesome_table["apple-pay"] = 0xf415; - font_awesome_table["archive"] = 0xf187; - font_awesome_table["archway"] = 0xf557; - font_awesome_table["arrow-alt-circle-down"] = 0xf358; - font_awesome_table["arrow-alt-circle-left"] = 0xf359; - font_awesome_table["arrow-alt-circle-right"] = 0xf35a; - font_awesome_table["arrow-alt-circle-up"] = 0xf35b; - font_awesome_table["arrow-circle-down"] = 0xf0ab; - font_awesome_table["arrow-circle-left"] = 0xf0a8; - font_awesome_table["arrow-circle-right"] = 0xf0a9; - font_awesome_table["arrow-circle-up"] = 0xf0aa; - font_awesome_table["arrow-down"] = 0xf063; - font_awesome_table["arrow-left"] = 0xf060; - font_awesome_table["arrow-right"] = 0xf061; - font_awesome_table["arrow-up"] = 0xf062; - font_awesome_table["arrows-alt"] = 0xf0b2; - font_awesome_table["arrows-alt-h"] = 0xf337; - font_awesome_table["arrows-alt-v"] = 0xf338; - font_awesome_table["assistive-listening-systems"] = 0xf2a2; - font_awesome_table["asterisk"] = 0xf069; - font_awesome_table["asymmetrik"] = 0xf372; - font_awesome_table["at"] = 0xf1fa; - font_awesome_table["atlas"] = 0xf558; - font_awesome_table["atom"] = 0xf5d2; - font_awesome_table["audible"] = 0xf373; - font_awesome_table["audio-description"] = 0xf29e; - font_awesome_table["autoprefixer"] = 0xf41c; - font_awesome_table["avianex"] = 0xf374; - font_awesome_table["aviato"] = 0xf421; - font_awesome_table["award"] = 0xf559; - font_awesome_table["aws"] = 0xf375; - font_awesome_table["backspace"] = 0xf55a; - font_awesome_table["backward"] = 0xf04a; - font_awesome_table["balance-scale"] = 0xf24e; - font_awesome_table["ban"] = 0xf05e; - font_awesome_table["band-aid"] = 0xf462; - font_awesome_table["bandcamp"] = 0xf2d5; - font_awesome_table["barcode"] = 0xf02a; - font_awesome_table["bars"] = 0xf0c9; - font_awesome_table["baseball-ball"] = 0xf433; - font_awesome_table["basketball-ball"] = 0xf434; - font_awesome_table["bath"] = 0xf2cd; - font_awesome_table["battery-empty"] = 0xf244; - font_awesome_table["battery-full"] = 0xf240; - font_awesome_table["battery-half"] = 0xf242; - font_awesome_table["battery-quarter"] = 0xf243; - font_awesome_table["battery-three-quarters"] = 0xf241; - font_awesome_table["bed"] = 0xf236; - font_awesome_table["beer"] = 0xf0fc; - font_awesome_table["behance"] = 0xf1b4; - font_awesome_table["behance-square"] = 0xf1b5; - font_awesome_table["bell"] = 0xf0f3; - font_awesome_table["bell-slash"] = 0xf1f6; - font_awesome_table["bezier-curve"] = 0xf55b; - font_awesome_table["bicycle"] = 0xf206; - font_awesome_table["bimobject"] = 0xf378; - font_awesome_table["binoculars"] = 0xf1e5; - font_awesome_table["birthday-cake"] = 0xf1fd; - font_awesome_table["bitbucket"] = 0xf171; - font_awesome_table["bitcoin"] = 0xf379; - font_awesome_table["bity"] = 0xf37a; - font_awesome_table["black-tie"] = 0xf27e; - font_awesome_table["blackberry"] = 0xf37b; - font_awesome_table["blender"] = 0xf517; - font_awesome_table["blind"] = 0xf29d; - font_awesome_table["blogger"] = 0xf37c; - font_awesome_table["blogger-b"] = 0xf37d; - font_awesome_table["bluetooth"] = 0xf293; - font_awesome_table["bluetooth-b"] = 0xf294; - font_awesome_table["bold"] = 0xf032; - font_awesome_table["bolt"] = 0xf0e7; - font_awesome_table["bomb"] = 0xf1e2; - font_awesome_table["bone"] = 0xf5d7; - font_awesome_table["bong"] = 0xf55c; - font_awesome_table["book"] = 0xf02d; - font_awesome_table["book-open"] = 0xf518; - font_awesome_table["book-reader"] = 0xf5da; - font_awesome_table["bookmark"] = 0xf02e; - font_awesome_table["bowling-ball"] = 0xf436; - font_awesome_table["box"] = 0xf466; - font_awesome_table["box-open"] = 0xf49e; - font_awesome_table["boxes"] = 0xf468; - font_awesome_table["braille"] = 0xf2a1; - font_awesome_table["brain"] = 0xf5dc; - font_awesome_table["briefcase"] = 0xf0b1; - font_awesome_table["briefcase-medical"] = 0xf469; - font_awesome_table["broadcast-tower"] = 0xf519; - font_awesome_table["broom"] = 0xf51a; - font_awesome_table["brush"] = 0xf55d; - font_awesome_table["btc"] = 0xf15a; - font_awesome_table["bug"] = 0xf188; - font_awesome_table["building"] = 0xf1ad; - font_awesome_table["bullhorn"] = 0xf0a1; - font_awesome_table["bullseye"] = 0xf140; - font_awesome_table["burn"] = 0xf46a; - font_awesome_table["buromobelexperte"] = 0xf37f; - font_awesome_table["bus"] = 0xf207; - font_awesome_table["bus-alt"] = 0xf55e; - font_awesome_table["buysellads"] = 0xf20d; - font_awesome_table["calculator"] = 0xf1ec; - font_awesome_table["calendar"] = 0xf133; - font_awesome_table["calendar-alt"] = 0xf073; - font_awesome_table["calendar-check"] = 0xf274; - font_awesome_table["calendar-minus"] = 0xf272; - font_awesome_table["calendar-plus"] = 0xf271; - font_awesome_table["calendar-times"] = 0xf273; - font_awesome_table["camera"] = 0xf030; - font_awesome_table["camera-retro"] = 0xf083; - font_awesome_table["cannabis"] = 0xf55f; - font_awesome_table["capsules"] = 0xf46b; - font_awesome_table["car"] = 0xf1b9; - font_awesome_table["car-alt"] = 0xf5de; - font_awesome_table["car-battery"] = 0xf5df; - font_awesome_table["car-crash"] = 0xf5e1; - font_awesome_table["car-side"] = 0xf5e4; - font_awesome_table["caret-down"] = 0xf0d7; - font_awesome_table["caret-left"] = 0xf0d9; - font_awesome_table["caret-right"] = 0xf0da; - font_awesome_table["caret-square-down"] = 0xf150; - font_awesome_table["caret-square-left"] = 0xf191; - font_awesome_table["caret-square-right"] = 0xf152; - font_awesome_table["caret-square-up"] = 0xf151; - font_awesome_table["caret-up"] = 0xf0d8; - font_awesome_table["cart-arrow-down"] = 0xf218; - font_awesome_table["cart-plus"] = 0xf217; - font_awesome_table["cc-amazon-pay"] = 0xf42d; - font_awesome_table["cc-amex"] = 0xf1f3; - font_awesome_table["cc-apple-pay"] = 0xf416; - font_awesome_table["cc-diners-club"] = 0xf24c; - font_awesome_table["cc-discover"] = 0xf1f2; - font_awesome_table["cc-jcb"] = 0xf24b; - font_awesome_table["cc-mastercard"] = 0xf1f1; - font_awesome_table["cc-paypal"] = 0xf1f4; - font_awesome_table["cc-stripe"] = 0xf1f5; - font_awesome_table["cc-visa"] = 0xf1f0; - font_awesome_table["centercode"] = 0xf380; - font_awesome_table["certificate"] = 0xf0a3; - font_awesome_table["chalkboard"] = 0xf51b; - font_awesome_table["chalkboard-teacher"] = 0xf51c; - font_awesome_table["charging-station"] = 0xf5e7; - font_awesome_table["chart-area"] = 0xf1fe; - font_awesome_table["chart-bar"] = 0xf080; - font_awesome_table["chart-line"] = 0xf201; - font_awesome_table["chart-pie"] = 0xf200; - font_awesome_table["check"] = 0xf00c; - font_awesome_table["check-circle"] = 0xf058; - font_awesome_table["check-double"] = 0xf560; - font_awesome_table["check-square"] = 0xf14a; - font_awesome_table["chess"] = 0xf439; - font_awesome_table["chess-bishop"] = 0xf43a; - font_awesome_table["chess-board"] = 0xf43c; - font_awesome_table["chess-king"] = 0xf43f; - font_awesome_table["chess-knight"] = 0xf441; - font_awesome_table["chess-pawn"] = 0xf443; - font_awesome_table["chess-queen"] = 0xf445; - font_awesome_table["chess-rook"] = 0xf447; - font_awesome_table["chevron-circle-down"] = 0xf13a; - font_awesome_table["chevron-circle-left"] = 0xf137; - font_awesome_table["chevron-circle-right"] = 0xf138; - font_awesome_table["chevron-circle-up"] = 0xf139; - font_awesome_table["chevron-down"] = 0xf078; - font_awesome_table["chevron-left"] = 0xf053; - font_awesome_table["chevron-right"] = 0xf054; - font_awesome_table["chevron-up"] = 0xf077; - font_awesome_table["child"] = 0xf1ae; - font_awesome_table["chrome"] = 0xf268; - font_awesome_table["church"] = 0xf51d; - font_awesome_table["circle"] = 0xf111; - font_awesome_table["circle-notch"] = 0xf1ce; - font_awesome_table["clipboard"] = 0xf328; - font_awesome_table["clipboard-check"] = 0xf46c; - font_awesome_table["clipboard-list"] = 0xf46d; - font_awesome_table["clock"] = 0xf017; - font_awesome_table["clone"] = 0xf24d; - font_awesome_table["closed-captioning"] = 0xf20a; - font_awesome_table["cloud"] = 0xf0c2; - font_awesome_table["cloud-download-alt"] = 0xf381; - font_awesome_table["cloud-upload-alt"] = 0xf382; - font_awesome_table["cloudscale"] = 0xf383; - font_awesome_table["cloudsmith"] = 0xf384; - font_awesome_table["cloudversify"] = 0xf385; - font_awesome_table["cocktail"] = 0xf561; - font_awesome_table["code"] = 0xf121; - font_awesome_table["code-branch"] = 0xf126; - font_awesome_table["codepen"] = 0xf1cb; - font_awesome_table["codiepie"] = 0xf284; - font_awesome_table["coffee"] = 0xf0f4; - font_awesome_table["cog"] = 0xf013; - font_awesome_table["cogs"] = 0xf085; - font_awesome_table["coins"] = 0xf51e; - font_awesome_table["columns"] = 0xf0db; - font_awesome_table["comment"] = 0xf075; - font_awesome_table["comment-alt"] = 0xf27a; - font_awesome_table["comment-dots"] = 0xf4ad; - font_awesome_table["comment-slash"] = 0xf4b3; - font_awesome_table["comments"] = 0xf086; - font_awesome_table["compact-disc"] = 0xf51f; - font_awesome_table["compass"] = 0xf14e; - font_awesome_table["compress"] = 0xf066; - font_awesome_table["concierge-bell"] = 0xf562; - font_awesome_table["connectdevelop"] = 0xf20e; - font_awesome_table["contao"] = 0xf26d; - font_awesome_table["cookie"] = 0xf563; - font_awesome_table["cookie-bite"] = 0xf564; - font_awesome_table["copy"] = 0xf0c5; - font_awesome_table["copyright"] = 0xf1f9; - font_awesome_table["couch"] = 0xf4b8; - font_awesome_table["cpanel"] = 0xf388; - font_awesome_table["creative-commons"] = 0xf25e; - font_awesome_table["creative-commons-by"] = 0xf4e7; - font_awesome_table["creative-commons-nc"] = 0xf4e8; - font_awesome_table["creative-commons-nc-eu"] = 0xf4e9; - font_awesome_table["creative-commons-nc-jp"] = 0xf4ea; - font_awesome_table["creative-commons-nd"] = 0xf4eb; - font_awesome_table["creative-commons-pd"] = 0xf4ec; - font_awesome_table["creative-commons-pd-alt"] = 0xf4ed; - font_awesome_table["creative-commons-remix"] = 0xf4ee; - font_awesome_table["creative-commons-sa"] = 0xf4ef; - font_awesome_table["creative-commons-sampling"] = 0xf4f0; - font_awesome_table["creative-commons-sampling-plus"] = 0xf4f1; - font_awesome_table["creative-commons-share"] = 0xf4f2; - font_awesome_table["credit-card"] = 0xf09d; - font_awesome_table["crop"] = 0xf125; - font_awesome_table["crop-alt"] = 0xf565; - font_awesome_table["crosshairs"] = 0xf05b; - font_awesome_table["crow"] = 0xf520; - font_awesome_table["crown"] = 0xf521; - font_awesome_table["css3"] = 0xf13c; - font_awesome_table["css3-alt"] = 0xf38b; - font_awesome_table["cube"] = 0xf1b2; - font_awesome_table["cubes"] = 0xf1b3; - font_awesome_table["cut"] = 0xf0c4; - font_awesome_table["cuttlefish"] = 0xf38c; - font_awesome_table["d-and-d"] = 0xf38d; - font_awesome_table["dashcube"] = 0xf210; - font_awesome_table["database"] = 0xf1c0; - font_awesome_table["deaf"] = 0xf2a4; - font_awesome_table["delicious"] = 0xf1a5; - font_awesome_table["deploydog"] = 0xf38e; - font_awesome_table["deskpro"] = 0xf38f; - font_awesome_table["desktop"] = 0xf108; - font_awesome_table["deviantart"] = 0xf1bd; - font_awesome_table["diagnoses"] = 0xf470; - font_awesome_table["dice"] = 0xf522; - font_awesome_table["dice-five"] = 0xf523; - font_awesome_table["dice-four"] = 0xf524; - font_awesome_table["dice-one"] = 0xf525; - font_awesome_table["dice-six"] = 0xf526; - font_awesome_table["dice-three"] = 0xf527; - font_awesome_table["dice-two"] = 0xf528; - font_awesome_table["digg"] = 0xf1a6; - font_awesome_table["digital-ocean"] = 0xf391; - font_awesome_table["digital-tachograph"] = 0xf566; - font_awesome_table["directions"] = 0xf5eb; - font_awesome_table["discord"] = 0xf392; - font_awesome_table["discourse"] = 0xf393; - font_awesome_table["divide"] = 0xf529; - font_awesome_table["dizzy"] = 0xf567; - font_awesome_table["dna"] = 0xf471; - font_awesome_table["dochub"] = 0xf394; - font_awesome_table["docker"] = 0xf395; - font_awesome_table["dollar-sign"] = 0xf155; - font_awesome_table["dolly"] = 0xf472; - font_awesome_table["dolly-flatbed"] = 0xf474; - font_awesome_table["donate"] = 0xf4b9; - font_awesome_table["door-closed"] = 0xf52a; - font_awesome_table["door-open"] = 0xf52b; - font_awesome_table["dot-circle"] = 0xf192; - font_awesome_table["dove"] = 0xf4ba; - font_awesome_table["download"] = 0xf019; - font_awesome_table["draft2digital"] = 0xf396; - font_awesome_table["drafting-compass"] = 0xf568; - font_awesome_table["draw-polygon"] = 0xf5ee; - font_awesome_table["dribbble"] = 0xf17d; - font_awesome_table["dribbble-square"] = 0xf397; - font_awesome_table["dropbox"] = 0xf16b; - font_awesome_table["drum"] = 0xf569; - font_awesome_table["drum-steelpan"] = 0xf56a; - font_awesome_table["drupal"] = 0xf1a9; - font_awesome_table["dumbbell"] = 0xf44b; - font_awesome_table["dyalog"] = 0xf399; - font_awesome_table["earlybirds"] = 0xf39a; - font_awesome_table["ebay"] = 0xf4f4; - font_awesome_table["edge"] = 0xf282; - font_awesome_table["edit"] = 0xf044; - font_awesome_table["eject"] = 0xf052; - font_awesome_table["elementor"] = 0xf430; - font_awesome_table["ellipsis-h"] = 0xf141; - font_awesome_table["ellipsis-v"] = 0xf142; - font_awesome_table["ello"] = 0xf5f1; - font_awesome_table["ember"] = 0xf423; - font_awesome_table["empire"] = 0xf1d1; - font_awesome_table["envelope"] = 0xf0e0; - font_awesome_table["envelope-open"] = 0xf2b6; - font_awesome_table["envelope-square"] = 0xf199; - font_awesome_table["envira"] = 0xf299; - font_awesome_table["equals"] = 0xf52c; - font_awesome_table["eraser"] = 0xf12d; - font_awesome_table["erlang"] = 0xf39d; - font_awesome_table["ethereum"] = 0xf42e; - font_awesome_table["etsy"] = 0xf2d7; - font_awesome_table["euro-sign"] = 0xf153; - font_awesome_table["exchange-alt"] = 0xf362; - font_awesome_table["exclamation"] = 0xf12a; - font_awesome_table["exclamation-circle"] = 0xf06a; - font_awesome_table["exclamation-triangle"] = 0xf071; - font_awesome_table["expand"] = 0xf065; - font_awesome_table["expand-arrows-alt"] = 0xf31e; - font_awesome_table["expeditedssl"] = 0xf23e; - font_awesome_table["external-link-alt"] = 0xf35d; - font_awesome_table["external-link-square-alt"] = 0xf360; - font_awesome_table["eye"] = 0xf06e; - font_awesome_table["eye-dropper"] = 0xf1fb; - font_awesome_table["eye-slash"] = 0xf070; - font_awesome_table["facebook"] = 0xf09a; - font_awesome_table["facebook-f"] = 0xf39e; - font_awesome_table["facebook-messenger"] = 0xf39f; - font_awesome_table["facebook-square"] = 0xf082; - font_awesome_table["fast-backward"] = 0xf049; - font_awesome_table["fast-forward"] = 0xf050; - font_awesome_table["fax"] = 0xf1ac; - font_awesome_table["feather"] = 0xf52d; - font_awesome_table["feather-alt"] = 0xf56b; - font_awesome_table["female"] = 0xf182; - font_awesome_table["fighter-jet"] = 0xf0fb; - font_awesome_table["file"] = 0xf15b; - font_awesome_table["file-alt"] = 0xf15c; - font_awesome_table["file-archive"] = 0xf1c6; - font_awesome_table["file-audio"] = 0xf1c7; - font_awesome_table["file-code"] = 0xf1c9; - font_awesome_table["file-contract"] = 0xf56c; - font_awesome_table["file-download"] = 0xf56d; - font_awesome_table["file-excel"] = 0xf1c3; - font_awesome_table["file-export"] = 0xf56e; - font_awesome_table["file-image"] = 0xf1c5; - font_awesome_table["file-import"] = 0xf56f; - font_awesome_table["file-invoice"] = 0xf570; - font_awesome_table["file-invoice-dollar"] = 0xf571; - font_awesome_table["file-medical"] = 0xf477; - font_awesome_table["file-medical-alt"] = 0xf478; - font_awesome_table["file-pdf"] = 0xf1c1; - font_awesome_table["file-powerpoint"] = 0xf1c4; - font_awesome_table["file-prescription"] = 0xf572; - font_awesome_table["file-signature"] = 0xf573; - font_awesome_table["file-upload"] = 0xf574; - font_awesome_table["file-video"] = 0xf1c8; - font_awesome_table["file-word"] = 0xf1c2; - font_awesome_table["fill"] = 0xf575; - font_awesome_table["fill-drip"] = 0xf576; - font_awesome_table["film"] = 0xf008; - font_awesome_table["filter"] = 0xf0b0; - font_awesome_table["fingerprint"] = 0xf577; - font_awesome_table["fire"] = 0xf06d; - font_awesome_table["fire-extinguisher"] = 0xf134; - font_awesome_table["firefox"] = 0xf269; - font_awesome_table["first-aid"] = 0xf479; - font_awesome_table["first-order"] = 0xf2b0; - font_awesome_table["first-order-alt"] = 0xf50a; - font_awesome_table["firstdraft"] = 0xf3a1; - font_awesome_table["fish"] = 0xf578; - font_awesome_table["flag"] = 0xf024; - font_awesome_table["flag-checkered"] = 0xf11e; - font_awesome_table["flask"] = 0xf0c3; - font_awesome_table["flickr"] = 0xf16e; - font_awesome_table["flipboard"] = 0xf44d; - font_awesome_table["flushed"] = 0xf579; - font_awesome_table["fly"] = 0xf417; - font_awesome_table["folder"] = 0xf07b; - font_awesome_table["folder-open"] = 0xf07c; - font_awesome_table["font"] = 0xf031; - font_awesome_table["font-awesome"] = 0xf2b4; - font_awesome_table["font-awesome-alt"] = 0xf35c; - font_awesome_table["font-awesome-flag"] = 0xf425; - font_awesome_table["font-awesome-logo-full"] = 0xf4e6; - font_awesome_table["fonticons"] = 0xf280; - font_awesome_table["fonticons-fi"] = 0xf3a2; - font_awesome_table["football-ball"] = 0xf44e; - font_awesome_table["fort-awesome"] = 0xf286; - font_awesome_table["fort-awesome-alt"] = 0xf3a3; - font_awesome_table["forumbee"] = 0xf211; - font_awesome_table["forward"] = 0xf04e; - font_awesome_table["foursquare"] = 0xf180; - font_awesome_table["free-code-camp"] = 0xf2c5; - font_awesome_table["freebsd"] = 0xf3a4; - font_awesome_table["frog"] = 0xf52e; - font_awesome_table["frown"] = 0xf119; - font_awesome_table["frown-open"] = 0xf57a; - font_awesome_table["fulcrum"] = 0xf50b; - font_awesome_table["futbol"] = 0xf1e3; - font_awesome_table["galactic-republic"] = 0xf50c; - font_awesome_table["galactic-senate"] = 0xf50d; - font_awesome_table["gamepad"] = 0xf11b; - font_awesome_table["gas-pump"] = 0xf52f; - font_awesome_table["gavel"] = 0xf0e3; - font_awesome_table["gem"] = 0xf3a5; - font_awesome_table["genderless"] = 0xf22d; - font_awesome_table["get-pocket"] = 0xf265; - font_awesome_table["gg"] = 0xf260; - font_awesome_table["gg-circle"] = 0xf261; - font_awesome_table["gift"] = 0xf06b; - font_awesome_table["git"] = 0xf1d3; - font_awesome_table["git-square"] = 0xf1d2; - font_awesome_table["github"] = 0xf09b; - font_awesome_table["github-alt"] = 0xf113; - font_awesome_table["github-square"] = 0xf092; - font_awesome_table["gitkraken"] = 0xf3a6; - font_awesome_table["gitlab"] = 0xf296; - font_awesome_table["gitter"] = 0xf426; - font_awesome_table["glass-martini"] = 0xf000; - font_awesome_table["glass-martini-alt"] = 0xf57b; - font_awesome_table["glasses"] = 0xf530; - font_awesome_table["glide"] = 0xf2a5; - font_awesome_table["glide-g"] = 0xf2a6; - font_awesome_table["globe"] = 0xf0ac; - font_awesome_table["globe-africa"] = 0xf57c; - font_awesome_table["globe-americas"] = 0xf57d; - font_awesome_table["globe-asia"] = 0xf57e; - font_awesome_table["gofore"] = 0xf3a7; - font_awesome_table["golf-ball"] = 0xf450; - font_awesome_table["goodreads"] = 0xf3a8; - font_awesome_table["goodreads-g"] = 0xf3a9; - font_awesome_table["google"] = 0xf1a0; - font_awesome_table["google-drive"] = 0xf3aa; - font_awesome_table["google-play"] = 0xf3ab; - font_awesome_table["google-plus"] = 0xf2b3; - font_awesome_table["google-plus-g"] = 0xf0d5; - font_awesome_table["google-plus-square"] = 0xf0d4; - font_awesome_table["google-wallet"] = 0xf1ee; - font_awesome_table["graduation-cap"] = 0xf19d; - font_awesome_table["gratipay"] = 0xf184; - font_awesome_table["grav"] = 0xf2d6; - font_awesome_table["greater-than"] = 0xf531; - font_awesome_table["greater-than-equal"] = 0xf532; - font_awesome_table["grimace"] = 0xf57f; - font_awesome_table["grin"] = 0xf580; - font_awesome_table["grin-alt"] = 0xf581; - font_awesome_table["grin-beam"] = 0xf582; - font_awesome_table["grin-beam-sweat"] = 0xf583; - font_awesome_table["grin-hearts"] = 0xf584; - font_awesome_table["grin-squint"] = 0xf585; - font_awesome_table["grin-squint-tears"] = 0xf586; - font_awesome_table["grin-stars"] = 0xf587; - font_awesome_table["grin-tears"] = 0xf588; - font_awesome_table["grin-tongue"] = 0xf589; - font_awesome_table["grin-tongue-squint"] = 0xf58a; - font_awesome_table["grin-tongue-wink"] = 0xf58b; - font_awesome_table["grin-wink"] = 0xf58c; - font_awesome_table["grip-horizontal"] = 0xf58d; - font_awesome_table["grip-vertical"] = 0xf58e; - font_awesome_table["gripfire"] = 0xf3ac; - font_awesome_table["grunt"] = 0xf3ad; - font_awesome_table["gulp"] = 0xf3ae; - font_awesome_table["h-square"] = 0xf0fd; - font_awesome_table["hacker-news"] = 0xf1d4; - font_awesome_table["hacker-news-square"] = 0xf3af; - font_awesome_table["hackerrank"] = 0xf5f7; - font_awesome_table["hand-holding"] = 0xf4bd; - font_awesome_table["hand-holding-heart"] = 0xf4be; - font_awesome_table["hand-holding-usd"] = 0xf4c0; - font_awesome_table["hand-lizard"] = 0xf258; - font_awesome_table["hand-paper"] = 0xf256; - font_awesome_table["hand-peace"] = 0xf25b; - font_awesome_table["hand-point-down"] = 0xf0a7; - font_awesome_table["hand-point-left"] = 0xf0a5; - font_awesome_table["hand-point-right"] = 0xf0a4; - font_awesome_table["hand-point-up"] = 0xf0a6; - font_awesome_table["hand-pointer"] = 0xf25a; - font_awesome_table["hand-rock"] = 0xf255; - font_awesome_table["hand-scissors"] = 0xf257; - font_awesome_table["hand-spock"] = 0xf259; - font_awesome_table["hands"] = 0xf4c2; - font_awesome_table["hands-helping"] = 0xf4c4; - font_awesome_table["handshake"] = 0xf2b5; - font_awesome_table["hashtag"] = 0xf292; - font_awesome_table["hdd"] = 0xf0a0; - font_awesome_table["heading"] = 0xf1dc; - font_awesome_table["headphones"] = 0xf025; - font_awesome_table["headphones-alt"] = 0xf58f; - font_awesome_table["headset"] = 0xf590; - font_awesome_table["heart"] = 0xf004; - font_awesome_table["heartbeat"] = 0xf21e; - font_awesome_table["helicopter"] = 0xf533; - font_awesome_table["highlighter"] = 0xf591; - font_awesome_table["hips"] = 0xf452; - font_awesome_table["hire-a-helper"] = 0xf3b0; - font_awesome_table["history"] = 0xf1da; - font_awesome_table["hockey-puck"] = 0xf453; - font_awesome_table["home"] = 0xf015; - font_awesome_table["hooli"] = 0xf427; - font_awesome_table["hornbill"] = 0xf592; - font_awesome_table["hospital"] = 0xf0f8; - font_awesome_table["hospital-alt"] = 0xf47d; - font_awesome_table["hospital-symbol"] = 0xf47e; - font_awesome_table["hot-tub"] = 0xf593; - font_awesome_table["hotel"] = 0xf594; - font_awesome_table["hotjar"] = 0xf3b1; - font_awesome_table["hourglass"] = 0xf254; - font_awesome_table["hourglass-end"] = 0xf253; - font_awesome_table["hourglass-half"] = 0xf252; - font_awesome_table["hourglass-start"] = 0xf251; - font_awesome_table["houzz"] = 0xf27c; - font_awesome_table["html5"] = 0xf13b; - font_awesome_table["hubspot"] = 0xf3b2; - font_awesome_table["i-cursor"] = 0xf246; - font_awesome_table["id-badge"] = 0xf2c1; - font_awesome_table["id-card"] = 0xf2c2; - font_awesome_table["id-card-alt"] = 0xf47f; - font_awesome_table["image"] = 0xf03e; - font_awesome_table["images"] = 0xf302; - font_awesome_table["imdb"] = 0xf2d8; - font_awesome_table["inbox"] = 0xf01c; - font_awesome_table["indent"] = 0xf03c; - font_awesome_table["industry"] = 0xf275; - font_awesome_table["infinity"] = 0xf534; - font_awesome_table["info"] = 0xf129; - font_awesome_table["info-circle"] = 0xf05a; - font_awesome_table["instagram"] = 0xf16d; - font_awesome_table["internet-explorer"] = 0xf26b; - font_awesome_table["ioxhost"] = 0xf208; - font_awesome_table["italic"] = 0xf033; - font_awesome_table["itunes"] = 0xf3b4; - font_awesome_table["itunes-note"] = 0xf3b5; - font_awesome_table["java"] = 0xf4e4; - font_awesome_table["jedi-order"] = 0xf50e; - font_awesome_table["jenkins"] = 0xf3b6; - font_awesome_table["joget"] = 0xf3b7; - font_awesome_table["joint"] = 0xf595; - font_awesome_table["joomla"] = 0xf1aa; - font_awesome_table["js"] = 0xf3b8; - font_awesome_table["js-square"] = 0xf3b9; - font_awesome_table["jsfiddle"] = 0xf1cc; - font_awesome_table["kaggle"] = 0xf5fa; - font_awesome_table["key"] = 0xf084; - font_awesome_table["keybase"] = 0xf4f5; - font_awesome_table["keyboard"] = 0xf11c; - font_awesome_table["keycdn"] = 0xf3ba; - font_awesome_table["kickstarter"] = 0xf3bb; - font_awesome_table["kickstarter-k"] = 0xf3bc; - font_awesome_table["kiss"] = 0xf596; - font_awesome_table["kiss-beam"] = 0xf597; - font_awesome_table["kiss-wink-heart"] = 0xf598; - font_awesome_table["kiwi-bird"] = 0xf535; - font_awesome_table["korvue"] = 0xf42f; - font_awesome_table["language"] = 0xf1ab; - font_awesome_table["laptop"] = 0xf109; - font_awesome_table["laptop-code"] = 0xf5fc; - font_awesome_table["laravel"] = 0xf3bd; - font_awesome_table["lastfm"] = 0xf202; - font_awesome_table["lastfm-square"] = 0xf203; - font_awesome_table["laugh"] = 0xf599; - font_awesome_table["laugh-beam"] = 0xf59a; - font_awesome_table["laugh-squint"] = 0xf59b; - font_awesome_table["laugh-wink"] = 0xf59c; - font_awesome_table["layer-group"] = 0xf5fd; - font_awesome_table["leaf"] = 0xf06c; - font_awesome_table["leanpub"] = 0xf212; - font_awesome_table["lemon"] = 0xf094; - font_awesome_table["less"] = 0xf41d; - font_awesome_table["less-than"] = 0xf536; - font_awesome_table["less-than-equal"] = 0xf537; - font_awesome_table["level-down-alt"] = 0xf3be; - font_awesome_table["level-up-alt"] = 0xf3bf; - font_awesome_table["life-ring"] = 0xf1cd; - font_awesome_table["lightbulb"] = 0xf0eb; - font_awesome_table["line"] = 0xf3c0; - font_awesome_table["link"] = 0xf0c1; - font_awesome_table["linkedin"] = 0xf08c; - font_awesome_table["linkedin-in"] = 0xf0e1; - font_awesome_table["linode"] = 0xf2b8; - font_awesome_table["linux"] = 0xf17c; - font_awesome_table["lira-sign"] = 0xf195; - font_awesome_table["list"] = 0xf03a; - font_awesome_table["list-alt"] = 0xf022; - font_awesome_table["list-ol"] = 0xf0cb; - font_awesome_table["list-ul"] = 0xf0ca; - font_awesome_table["location-arrow"] = 0xf124; - font_awesome_table["lock"] = 0xf023; - font_awesome_table["lock-open"] = 0xf3c1; - font_awesome_table["long-arrow-alt-down"] = 0xf309; - font_awesome_table["long-arrow-alt-left"] = 0xf30a; - font_awesome_table["long-arrow-alt-right"] = 0xf30b; - font_awesome_table["long-arrow-alt-up"] = 0xf30c; - font_awesome_table["low-vision"] = 0xf2a8; - font_awesome_table["luggage-cart"] = 0xf59d; - font_awesome_table["lyft"] = 0xf3c3; - font_awesome_table["magento"] = 0xf3c4; - font_awesome_table["magic"] = 0xf0d0; - font_awesome_table["magnet"] = 0xf076; - font_awesome_table["mailchimp"] = 0xf59e; - font_awesome_table["male"] = 0xf183; - font_awesome_table["mandalorian"] = 0xf50f; - font_awesome_table["map"] = 0xf279; - font_awesome_table["map-marked"] = 0xf59f; - font_awesome_table["map-marked-alt"] = 0xf5a0; - font_awesome_table["map-marker"] = 0xf041; - font_awesome_table["map-marker-alt"] = 0xf3c5; - font_awesome_table["map-pin"] = 0xf276; - font_awesome_table["map-signs"] = 0xf277; - font_awesome_table["markdown"] = 0xf60f; - font_awesome_table["marker"] = 0xf5a1; - font_awesome_table["mars"] = 0xf222; - font_awesome_table["mars-double"] = 0xf227; - font_awesome_table["mars-stroke"] = 0xf229; - font_awesome_table["mars-stroke-h"] = 0xf22b; - font_awesome_table["mars-stroke-v"] = 0xf22a; - font_awesome_table["mastodon"] = 0xf4f6; - font_awesome_table["maxcdn"] = 0xf136; - font_awesome_table["medal"] = 0xf5a2; - font_awesome_table["medapps"] = 0xf3c6; - font_awesome_table["medium"] = 0xf23a; - font_awesome_table["medium-m"] = 0xf3c7; - font_awesome_table["medkit"] = 0xf0fa; - font_awesome_table["medrt"] = 0xf3c8; - font_awesome_table["meetup"] = 0xf2e0; - font_awesome_table["megaport"] = 0xf5a3; - font_awesome_table["meh"] = 0xf11a; - font_awesome_table["meh-blank"] = 0xf5a4; - font_awesome_table["meh-rolling-eyes"] = 0xf5a5; - font_awesome_table["memory"] = 0xf538; - font_awesome_table["mercury"] = 0xf223; - font_awesome_table["microchip"] = 0xf2db; - font_awesome_table["microphone"] = 0xf130; - font_awesome_table["microphone-alt"] = 0xf3c9; - font_awesome_table["microphone-alt-slash"] = 0xf539; - font_awesome_table["microphone-slash"] = 0xf131; - font_awesome_table["microscope"] = 0xf610; - font_awesome_table["microsoft"] = 0xf3ca; - font_awesome_table["minus"] = 0xf068; - font_awesome_table["minus-circle"] = 0xf056; - font_awesome_table["minus-square"] = 0xf146; - font_awesome_table["mix"] = 0xf3cb; - font_awesome_table["mixcloud"] = 0xf289; - font_awesome_table["mizuni"] = 0xf3cc; - font_awesome_table["mobile"] = 0xf10b; - font_awesome_table["mobile-alt"] = 0xf3cd; - font_awesome_table["modx"] = 0xf285; - font_awesome_table["monero"] = 0xf3d0; - font_awesome_table["money-bill"] = 0xf0d6; - font_awesome_table["money-bill-alt"] = 0xf3d1; - font_awesome_table["money-bill-wave"] = 0xf53a; - font_awesome_table["money-bill-wave-alt"] = 0xf53b; - font_awesome_table["money-check"] = 0xf53c; - font_awesome_table["money-check-alt"] = 0xf53d; - font_awesome_table["monument"] = 0xf5a6; - font_awesome_table["moon"] = 0xf186; - font_awesome_table["mortar-pestle"] = 0xf5a7; - font_awesome_table["motorcycle"] = 0xf21c; - font_awesome_table["mouse-pointer"] = 0xf245; - font_awesome_table["music"] = 0xf001; - font_awesome_table["napster"] = 0xf3d2; - font_awesome_table["neos"] = 0xf612; - font_awesome_table["neuter"] = 0xf22c; - font_awesome_table["newspaper"] = 0xf1ea; - font_awesome_table["nimblr"] = 0xf5a8; - font_awesome_table["nintendo-switch"] = 0xf418; - font_awesome_table["node"] = 0xf419; - font_awesome_table["node-js"] = 0xf3d3; - font_awesome_table["not-equal"] = 0xf53e; - font_awesome_table["notes-medical"] = 0xf481; - font_awesome_table["npm"] = 0xf3d4; - font_awesome_table["ns8"] = 0xf3d5; - font_awesome_table["nutritionix"] = 0xf3d6; - font_awesome_table["object-group"] = 0xf247; - font_awesome_table["object-ungroup"] = 0xf248; - font_awesome_table["odnoklassniki"] = 0xf263; - font_awesome_table["odnoklassniki-square"] = 0xf264; - font_awesome_table["oil-can"] = 0xf613; - font_awesome_table["old-republic"] = 0xf510; - font_awesome_table["opencart"] = 0xf23d; - font_awesome_table["openid"] = 0xf19b; - font_awesome_table["opera"] = 0xf26a; - font_awesome_table["optin-monster"] = 0xf23c; - font_awesome_table["osi"] = 0xf41a; - font_awesome_table["outdent"] = 0xf03b; - font_awesome_table["page4"] = 0xf3d7; - font_awesome_table["pagelines"] = 0xf18c; - font_awesome_table["paint-brush"] = 0xf1fc; - font_awesome_table["paint-roller"] = 0xf5aa; - font_awesome_table["palette"] = 0xf53f; - font_awesome_table["palfed"] = 0xf3d8; - font_awesome_table["pallet"] = 0xf482; - font_awesome_table["paper-plane"] = 0xf1d8; - font_awesome_table["paperclip"] = 0xf0c6; - font_awesome_table["parachute-box"] = 0xf4cd; - font_awesome_table["paragraph"] = 0xf1dd; - font_awesome_table["parking"] = 0xf540; - font_awesome_table["passport"] = 0xf5ab; - font_awesome_table["paste"] = 0xf0ea; - font_awesome_table["patreon"] = 0xf3d9; - font_awesome_table["pause"] = 0xf04c; - font_awesome_table["pause-circle"] = 0xf28b; - font_awesome_table["paw"] = 0xf1b0; - font_awesome_table["paypal"] = 0xf1ed; - font_awesome_table["pen"] = 0xf304; - font_awesome_table["pen-alt"] = 0xf305; - font_awesome_table["pen-fancy"] = 0xf5ac; - font_awesome_table["pen-nib"] = 0xf5ad; - font_awesome_table["pen-square"] = 0xf14b; - font_awesome_table["pencil-alt"] = 0xf303; - font_awesome_table["pencil-ruler"] = 0xf5ae; - font_awesome_table["people-carry"] = 0xf4ce; - font_awesome_table["percent"] = 0xf295; - font_awesome_table["percentage"] = 0xf541; - font_awesome_table["periscope"] = 0xf3da; - font_awesome_table["phabricator"] = 0xf3db; - font_awesome_table["phoenix-framework"] = 0xf3dc; - font_awesome_table["phoenix-squadron"] = 0xf511; - font_awesome_table["phone"] = 0xf095; - font_awesome_table["phone-slash"] = 0xf3dd; - font_awesome_table["phone-square"] = 0xf098; - font_awesome_table["phone-volume"] = 0xf2a0; - font_awesome_table["php"] = 0xf457; - font_awesome_table["pied-piper"] = 0xf2ae; - font_awesome_table["pied-piper-alt"] = 0xf1a8; - font_awesome_table["pied-piper-hat"] = 0xf4e5; - font_awesome_table["pied-piper-pp"] = 0xf1a7; - font_awesome_table["piggy-bank"] = 0xf4d3; - font_awesome_table["pills"] = 0xf484; - font_awesome_table["pinterest"] = 0xf0d2; - font_awesome_table["pinterest-p"] = 0xf231; - font_awesome_table["pinterest-square"] = 0xf0d3; - font_awesome_table["plane"] = 0xf072; - font_awesome_table["plane-arrival"] = 0xf5af; - font_awesome_table["plane-departure"] = 0xf5b0; - font_awesome_table["play"] = 0xf04b; - font_awesome_table["play-circle"] = 0xf144; - font_awesome_table["playstation"] = 0xf3df; - font_awesome_table["plug"] = 0xf1e6; - font_awesome_table["plus"] = 0xf067; - font_awesome_table["plus-circle"] = 0xf055; - font_awesome_table["plus-square"] = 0xf0fe; - font_awesome_table["podcast"] = 0xf2ce; - font_awesome_table["poo"] = 0xf2fe; - font_awesome_table["poop"] = 0xf619; - font_awesome_table["portrait"] = 0xf3e0; - font_awesome_table["pound-sign"] = 0xf154; - font_awesome_table["power-off"] = 0xf011; - font_awesome_table["prescription"] = 0xf5b1; - font_awesome_table["prescription-bottle"] = 0xf485; - font_awesome_table["prescription-bottle-alt"] = 0xf486; - font_awesome_table["print"] = 0xf02f; - font_awesome_table["procedures"] = 0xf487; - font_awesome_table["product-hunt"] = 0xf288; - font_awesome_table["project-diagram"] = 0xf542; - font_awesome_table["pushed"] = 0xf3e1; - font_awesome_table["puzzle-piece"] = 0xf12e; - font_awesome_table["python"] = 0xf3e2; - font_awesome_table["qq"] = 0xf1d6; - font_awesome_table["qrcode"] = 0xf029; - font_awesome_table["question"] = 0xf128; - font_awesome_table["question-circle"] = 0xf059; - font_awesome_table["quidditch"] = 0xf458; - font_awesome_table["quinscape"] = 0xf459; - font_awesome_table["quora"] = 0xf2c4; - font_awesome_table["quote-left"] = 0xf10d; - font_awesome_table["quote-right"] = 0xf10e; - font_awesome_table["r-project"] = 0xf4f7; - font_awesome_table["random"] = 0xf074; - font_awesome_table["ravelry"] = 0xf2d9; - font_awesome_table["react"] = 0xf41b; - font_awesome_table["readme"] = 0xf4d5; - font_awesome_table["rebel"] = 0xf1d0; - font_awesome_table["receipt"] = 0xf543; - font_awesome_table["recycle"] = 0xf1b8; - font_awesome_table["red-river"] = 0xf3e3; - font_awesome_table["reddit"] = 0xf1a1; - font_awesome_table["reddit-alien"] = 0xf281; - font_awesome_table["reddit-square"] = 0xf1a2; - font_awesome_table["redo"] = 0xf01e; - font_awesome_table["redo-alt"] = 0xf2f9; - font_awesome_table["registered"] = 0xf25d; - font_awesome_table["rendact"] = 0xf3e4; - font_awesome_table["renren"] = 0xf18b; - font_awesome_table["reply"] = 0xf3e5; - font_awesome_table["reply-all"] = 0xf122; - font_awesome_table["replyd"] = 0xf3e6; - font_awesome_table["researchgate"] = 0xf4f8; - font_awesome_table["resolving"] = 0xf3e7; - font_awesome_table["retweet"] = 0xf079; - font_awesome_table["rev"] = 0xf5b2; - font_awesome_table["ribbon"] = 0xf4d6; - font_awesome_table["road"] = 0xf018; - font_awesome_table["robot"] = 0xf544; - font_awesome_table["rocket"] = 0xf135; - font_awesome_table["rocketchat"] = 0xf3e8; - font_awesome_table["rockrms"] = 0xf3e9; - font_awesome_table["route"] = 0xf4d7; - font_awesome_table["rss"] = 0xf09e; - font_awesome_table["rss-square"] = 0xf143; - font_awesome_table["ruble-sign"] = 0xf158; - font_awesome_table["ruler"] = 0xf545; - font_awesome_table["ruler-combined"] = 0xf546; - font_awesome_table["ruler-horizontal"] = 0xf547; - font_awesome_table["ruler-vertical"] = 0xf548; - font_awesome_table["rupee-sign"] = 0xf156; - font_awesome_table["sad-cry"] = 0xf5b3; - font_awesome_table["sad-tear"] = 0xf5b4; - font_awesome_table["safari"] = 0xf267; - font_awesome_table["sass"] = 0xf41e; - font_awesome_table["save"] = 0xf0c7; - font_awesome_table["schlix"] = 0xf3ea; - font_awesome_table["school"] = 0xf549; - font_awesome_table["screwdriver"] = 0xf54a; - font_awesome_table["scribd"] = 0xf28a; - font_awesome_table["search"] = 0xf002; - font_awesome_table["search-minus"] = 0xf010; - font_awesome_table["search-plus"] = 0xf00e; - font_awesome_table["searchengin"] = 0xf3eb; - font_awesome_table["seedling"] = 0xf4d8; - font_awesome_table["sellcast"] = 0xf2da; - font_awesome_table["sellsy"] = 0xf213; - font_awesome_table["server"] = 0xf233; - font_awesome_table["servicestack"] = 0xf3ec; - font_awesome_table["shapes"] = 0xf61f; - font_awesome_table["share"] = 0xf064; - font_awesome_table["share-alt"] = 0xf1e0; - font_awesome_table["share-alt-square"] = 0xf1e1; - font_awesome_table["share-square"] = 0xf14d; - font_awesome_table["shekel-sign"] = 0xf20b; - font_awesome_table["shield-alt"] = 0xf3ed; - font_awesome_table["ship"] = 0xf21a; - font_awesome_table["shipping-fast"] = 0xf48b; - font_awesome_table["shirtsinbulk"] = 0xf214; - font_awesome_table["shoe-prints"] = 0xf54b; - font_awesome_table["shopping-bag"] = 0xf290; - font_awesome_table["shopping-basket"] = 0xf291; - font_awesome_table["shopping-cart"] = 0xf07a; - font_awesome_table["shopware"] = 0xf5b5; - font_awesome_table["shower"] = 0xf2cc; - font_awesome_table["shuttle-van"] = 0xf5b6; - font_awesome_table["sign"] = 0xf4d9; - font_awesome_table["sign-in-alt"] = 0xf2f6; - font_awesome_table["sign-language"] = 0xf2a7; - font_awesome_table["sign-out-alt"] = 0xf2f5; - font_awesome_table["signal"] = 0xf012; - font_awesome_table["signature"] = 0xf5b7; - font_awesome_table["simplybuilt"] = 0xf215; - font_awesome_table["sistrix"] = 0xf3ee; - font_awesome_table["sitemap"] = 0xf0e8; - font_awesome_table["sith"] = 0xf512; - font_awesome_table["skull"] = 0xf54c; - font_awesome_table["skyatlas"] = 0xf216; - font_awesome_table["skype"] = 0xf17e; - font_awesome_table["slack"] = 0xf198; - font_awesome_table["slack-hash"] = 0xf3ef; - font_awesome_table["sliders-h"] = 0xf1de; - font_awesome_table["slideshare"] = 0xf1e7; - font_awesome_table["smile"] = 0xf118; - font_awesome_table["smile-beam"] = 0xf5b8; - font_awesome_table["smile-wink"] = 0xf4da; - font_awesome_table["smoking"] = 0xf48d; - font_awesome_table["smoking-ban"] = 0xf54d; - font_awesome_table["snapchat"] = 0xf2ab; - font_awesome_table["snapchat-ghost"] = 0xf2ac; - font_awesome_table["snapchat-square"] = 0xf2ad; - font_awesome_table["snowflake"] = 0xf2dc; - font_awesome_table["solar-panel"] = 0xf5ba; - font_awesome_table["sort"] = 0xf0dc; - font_awesome_table["sort-alpha-down"] = 0xf15d; - font_awesome_table["sort-alpha-up"] = 0xf15e; - font_awesome_table["sort-amount-down"] = 0xf160; - font_awesome_table["sort-amount-up"] = 0xf161; - font_awesome_table["sort-down"] = 0xf0dd; - font_awesome_table["sort-numeric-down"] = 0xf162; - font_awesome_table["sort-numeric-up"] = 0xf163; - font_awesome_table["sort-up"] = 0xf0de; - font_awesome_table["soundcloud"] = 0xf1be; - font_awesome_table["spa"] = 0xf5bb; - font_awesome_table["space-shuttle"] = 0xf197; - font_awesome_table["speakap"] = 0xf3f3; - font_awesome_table["spinner"] = 0xf110; - font_awesome_table["splotch"] = 0xf5bc; - font_awesome_table["spotify"] = 0xf1bc; - font_awesome_table["spray-can"] = 0xf5bd; - font_awesome_table["square"] = 0xf0c8; - font_awesome_table["square-full"] = 0xf45c; - font_awesome_table["squarespace"] = 0xf5be; - font_awesome_table["stack-exchange"] = 0xf18d; - font_awesome_table["stack-overflow"] = 0xf16c; - font_awesome_table["stamp"] = 0xf5bf; - font_awesome_table["star"] = 0xf005; - font_awesome_table["star-half"] = 0xf089; - font_awesome_table["star-half-alt"] = 0xf5c0; - font_awesome_table["star-of-life"] = 0xf621; - font_awesome_table["staylinked"] = 0xf3f5; - font_awesome_table["steam"] = 0xf1b6; - font_awesome_table["steam-square"] = 0xf1b7; - font_awesome_table["steam-symbol"] = 0xf3f6; - font_awesome_table["step-backward"] = 0xf048; - font_awesome_table["step-forward"] = 0xf051; - font_awesome_table["stethoscope"] = 0xf0f1; - font_awesome_table["sticker-mule"] = 0xf3f7; - font_awesome_table["sticky-note"] = 0xf249; - font_awesome_table["stop"] = 0xf04d; - font_awesome_table["stop-circle"] = 0xf28d; - font_awesome_table["stopwatch"] = 0xf2f2; - font_awesome_table["store"] = 0xf54e; - font_awesome_table["store-alt"] = 0xf54f; - font_awesome_table["strava"] = 0xf428; - font_awesome_table["stream"] = 0xf550; - font_awesome_table["street-view"] = 0xf21d; - font_awesome_table["strikethrough"] = 0xf0cc; - font_awesome_table["stripe"] = 0xf429; - font_awesome_table["stripe-s"] = 0xf42a; - font_awesome_table["stroopwafel"] = 0xf551; - font_awesome_table["studiovinari"] = 0xf3f8; - font_awesome_table["stumbleupon"] = 0xf1a4; - font_awesome_table["stumbleupon-circle"] = 0xf1a3; - font_awesome_table["subscript"] = 0xf12c; - font_awesome_table["subway"] = 0xf239; - font_awesome_table["suitcase"] = 0xf0f2; - font_awesome_table["suitcase-rolling"] = 0xf5c1; - font_awesome_table["sun"] = 0xf185; - font_awesome_table["superpowers"] = 0xf2dd; - font_awesome_table["superscript"] = 0xf12b; - font_awesome_table["supple"] = 0xf3f9; - font_awesome_table["surprise"] = 0xf5c2; - font_awesome_table["swatchbook"] = 0xf5c3; - font_awesome_table["swimmer"] = 0xf5c4; - font_awesome_table["swimming-pool"] = 0xf5c5; - font_awesome_table["sync"] = 0xf021; - font_awesome_table["sync-alt"] = 0xf2f1; - font_awesome_table["syringe"] = 0xf48e; - font_awesome_table["font_awesome_table"] = 0xf0ce; - font_awesome_table["font_awesome_table-tennis"] = 0xf45d; - font_awesome_table["font_awesome_tablet"] = 0xf10a; - font_awesome_table["font_awesome_tablet-alt"] = 0xf3fa; - font_awesome_table["font_awesome_tablets"] = 0xf490; - font_awesome_table["tachometer-alt"] = 0xf3fd; - font_awesome_table["tag"] = 0xf02b; - font_awesome_table["tags"] = 0xf02c; - font_awesome_table["tape"] = 0xf4db; - font_awesome_table["tasks"] = 0xf0ae; - font_awesome_table["taxi"] = 0xf1ba; - font_awesome_table["teamspeak"] = 0xf4f9; - font_awesome_table["teeth"] = 0xf62e; - font_awesome_table["teeth-open"] = 0xf62f; - font_awesome_table["telegram"] = 0xf2c6; - font_awesome_table["telegram-plane"] = 0xf3fe; - font_awesome_table["tencent-weibo"] = 0xf1d5; - font_awesome_table["terminal"] = 0xf120; - font_awesome_table["text-height"] = 0xf034; - font_awesome_table["text-width"] = 0xf035; - font_awesome_table["th"] = 0xf00a; - font_awesome_table["th-large"] = 0xf009; - font_awesome_table["th-list"] = 0xf00b; - font_awesome_table["theater-masks"] = 0xf630; - font_awesome_table["themeco"] = 0xf5c6; - font_awesome_table["themeisle"] = 0xf2b2; - font_awesome_table["thermometer"] = 0xf491; - font_awesome_table["thermometer-empty"] = 0xf2cb; - font_awesome_table["thermometer-full"] = 0xf2c7; - font_awesome_table["thermometer-half"] = 0xf2c9; - font_awesome_table["thermometer-quarter"] = 0xf2ca; - font_awesome_table["thermometer-three-quarters"] = 0xf2c8; - font_awesome_table["thumbs-down"] = 0xf165; - font_awesome_table["thumbs-up"] = 0xf164; - font_awesome_table["thumbtack"] = 0xf08d; - font_awesome_table["ticket-alt"] = 0xf3ff; - font_awesome_table["times"] = 0xf00d; - font_awesome_table["times-circle"] = 0xf057; - font_awesome_table["tint"] = 0xf043; - font_awesome_table["tint-slash"] = 0xf5c7; - font_awesome_table["tired"] = 0xf5c8; - font_awesome_table["toggle-off"] = 0xf204; - font_awesome_table["toggle-on"] = 0xf205; - font_awesome_table["toolbox"] = 0xf552; - font_awesome_table["tooth"] = 0xf5c9; - font_awesome_table["trade-federation"] = 0xf513; - font_awesome_table["trademark"] = 0xf25c; - font_awesome_table["traffic-light"] = 0xf637; - font_awesome_table["train"] = 0xf238; - font_awesome_table["transgender"] = 0xf224; - font_awesome_table["transgender-alt"] = 0xf225; - font_awesome_table["trash"] = 0xf1f8; - font_awesome_table["trash-alt"] = 0xf2ed; - font_awesome_table["tree"] = 0xf1bb; - font_awesome_table["trello"] = 0xf181; - font_awesome_table["tripadvisor"] = 0xf262; - font_awesome_table["trophy"] = 0xf091; - font_awesome_table["truck"] = 0xf0d1; - font_awesome_table["truck-loading"] = 0xf4de; - font_awesome_table["truck-monster"] = 0xf63b; - font_awesome_table["truck-moving"] = 0xf4df; - font_awesome_table["truck-pickup"] = 0xf63c; - font_awesome_table["tshirt"] = 0xf553; - font_awesome_table["tty"] = 0xf1e4; - font_awesome_table["tumblr"] = 0xf173; - font_awesome_table["tumblr-square"] = 0xf174; - font_awesome_table["tv"] = 0xf26c; - font_awesome_table["twitch"] = 0xf1e8; - font_awesome_table["twitter"] = 0xf099; - font_awesome_table["twitter-square"] = 0xf081; - font_awesome_table["typo3"] = 0xf42b; - font_awesome_table["uber"] = 0xf402; - font_awesome_table["uikit"] = 0xf403; - font_awesome_table["umbrella"] = 0xf0e9; - font_awesome_table["umbrella-beach"] = 0xf5ca; - font_awesome_table["underline"] = 0xf0cd; - font_awesome_table["undo"] = 0xf0e2; - font_awesome_table["undo-alt"] = 0xf2ea; - font_awesome_table["uniregistry"] = 0xf404; - font_awesome_table["universal-access"] = 0xf29a; - font_awesome_table["university"] = 0xf19c; - font_awesome_table["unlink"] = 0xf127; - font_awesome_table["unlock"] = 0xf09c; - font_awesome_table["unlock-alt"] = 0xf13e; - font_awesome_table["untappd"] = 0xf405; - font_awesome_table["upload"] = 0xf093; - font_awesome_table["usb"] = 0xf287; - font_awesome_table["user"] = 0xf007; - font_awesome_table["user-alt"] = 0xf406; - font_awesome_table["user-alt-slash"] = 0xf4fa; - font_awesome_table["user-astronaut"] = 0xf4fb; - font_awesome_table["user-check"] = 0xf4fc; - font_awesome_table["user-circle"] = 0xf2bd; - font_awesome_table["user-clock"] = 0xf4fd; - font_awesome_table["user-cog"] = 0xf4fe; - font_awesome_table["user-edit"] = 0xf4ff; - font_awesome_table["user-friends"] = 0xf500; - font_awesome_table["user-graduate"] = 0xf501; - font_awesome_table["user-lock"] = 0xf502; - font_awesome_table["user-md"] = 0xf0f0; - font_awesome_table["user-minus"] = 0xf503; - font_awesome_table["user-ninja"] = 0xf504; - font_awesome_table["user-plus"] = 0xf234; - font_awesome_table["user-secret"] = 0xf21b; - font_awesome_table["user-shield"] = 0xf505; - font_awesome_table["user-slash"] = 0xf506; - font_awesome_table["user-tag"] = 0xf507; - font_awesome_table["user-tie"] = 0xf508; - font_awesome_table["user-times"] = 0xf235; - font_awesome_table["users"] = 0xf0c0; - font_awesome_table["users-cog"] = 0xf509; - font_awesome_table["ussunnah"] = 0xf407; - font_awesome_table["utensil-spoon"] = 0xf2e5; - font_awesome_table["utensils"] = 0xf2e7; - font_awesome_table["vaadin"] = 0xf408; - font_awesome_table["vector-square"] = 0xf5cb; - font_awesome_table["venus"] = 0xf221; - font_awesome_table["venus-double"] = 0xf226; - font_awesome_table["venus-mars"] = 0xf228; - font_awesome_table["viacoin"] = 0xf237; - font_awesome_table["viadeo"] = 0xf2a9; - font_awesome_table["viadeo-square"] = 0xf2aa; - font_awesome_table["vial"] = 0xf492; - font_awesome_table["vials"] = 0xf493; - font_awesome_table["viber"] = 0xf409; - font_awesome_table["video"] = 0xf03d; - font_awesome_table["video-slash"] = 0xf4e2; - font_awesome_table["vimeo"] = 0xf40a; - font_awesome_table["vimeo-square"] = 0xf194; - font_awesome_table["vimeo-v"] = 0xf27d; - font_awesome_table["vine"] = 0xf1ca; - font_awesome_table["vk"] = 0xf189; - font_awesome_table["vnv"] = 0xf40b; - font_awesome_table["volleyball-ball"] = 0xf45f; - font_awesome_table["volume-down"] = 0xf027; - font_awesome_table["volume-off"] = 0xf026; - font_awesome_table["volume-up"] = 0xf028; - font_awesome_table["vuejs"] = 0xf41f; - font_awesome_table["walking"] = 0xf554; - font_awesome_table["wallet"] = 0xf555; - font_awesome_table["warehouse"] = 0xf494; - font_awesome_table["weebly"] = 0xf5cc; - font_awesome_table["weibo"] = 0xf18a; - font_awesome_table["weight"] = 0xf496; - font_awesome_table["weight-hanging"] = 0xf5cd; - font_awesome_table["weixin"] = 0xf1d7; - font_awesome_table["whatsapp"] = 0xf232; - font_awesome_table["whatsapp-square"] = 0xf40c; - font_awesome_table["wheelchair"] = 0xf193; - font_awesome_table["whmcs"] = 0xf40d; - font_awesome_table["wifi"] = 0xf1eb; - font_awesome_table["wikipedia-w"] = 0xf266; - font_awesome_table["window-close"] = 0xf410; - font_awesome_table["window-maximize"] = 0xf2d0; - font_awesome_table["window-minimize"] = 0xf2d1; - font_awesome_table["window-restore"] = 0xf2d2; - font_awesome_table["windows"] = 0xf17a; - font_awesome_table["wine-glass"] = 0xf4e3; - font_awesome_table["wine-glass-alt"] = 0xf5ce; - font_awesome_table["wix"] = 0xf5cf; - font_awesome_table["wolf-pack-battalion"] = 0xf514; - font_awesome_table["won-sign"] = 0xf159; - font_awesome_table["wordpress"] = 0xf19a; - font_awesome_table["wordpress-simple"] = 0xf411; - font_awesome_table["wpbeginner"] = 0xf297; - font_awesome_table["wpexplorer"] = 0xf2de; - font_awesome_table["wpforms"] = 0xf298; - font_awesome_table["wrench"] = 0xf0ad; - font_awesome_table["x-ray"] = 0xf497; - font_awesome_table["xbox"] = 0xf412; - font_awesome_table["xing"] = 0xf168; - font_awesome_table["xing-square"] = 0xf169; - font_awesome_table["y-combinator"] = 0xf23b; - font_awesome_table["yahoo"] = 0xf19e; - font_awesome_table["yandex"] = 0xf413; - font_awesome_table["yandex-international"] = 0xf414; - font_awesome_table["yelp"] = 0xf1e9; - font_awesome_table["yen-sign"] = 0xf157; - font_awesome_table["yoast"] = 0xf2b1; - font_awesome_table["youtube"] = 0xf167; - font_awesome_table["youtube-square"] = 0xf431; - font_awesome_table["zhihu"] = 0xf63f; - } + lua_pushliteral(L,"BeginTabItem"); + lua_pushcfunction(L,wrapper_BeginTabItem); + lua_settable(L,-3); + lua_pop(L,1); } + diff --git a/src/lib/geogram_gfx/lua/lua_imgui.h b/src/lib/geogram_gfx/lua/lua_imgui.h old mode 100755 new mode 100644 index 422a453b..a48bc483 --- a/src/lib/geogram_gfx/lua/lua_imgui.h +++ b/src/lib/geogram_gfx/lua/lua_imgui.h @@ -52,7 +52,7 @@ extern "C" { #endif #include - + /** * \file geogram_gfx/lua/lua_imgui.h * \brief LUA bindings for ImGUI. diff --git a/src/lib/geogram_gfx/mesh/mesh_gfx.cpp b/src/lib/geogram_gfx/mesh/mesh_gfx.cpp index 9f4e7895..312e3192 100644 --- a/src/lib/geogram_gfx/mesh/mesh_gfx.cpp +++ b/src/lib/geogram_gfx/mesh/mesh_gfx.cpp @@ -108,7 +108,6 @@ namespace GEO { attribute_repeat_ = 1; attribute_dim_ = 1; - auto_GL_interop_ = false; ES_profile_ = false; } @@ -1061,11 +1060,6 @@ namespace GEO { if(glupCurrentContext() == nullptr) { glupMakeCurrent(glupCreateContext()); - auto_GL_interop_ = true; - } - - if(auto_GL_interop_) { - glupCopyFromGLState(GLUP_ALL_ATTRIBUTES); } if(show_mesh_) { diff --git a/src/lib/geogram_gfx/mesh/mesh_gfx.h b/src/lib/geogram_gfx/mesh/mesh_gfx.h index 689f61c0..0dc7f4d6 100644 --- a/src/lib/geogram_gfx/mesh/mesh_gfx.h +++ b/src/lib/geogram_gfx/mesh/mesh_gfx.h @@ -922,10 +922,6 @@ namespace GEO { index_t attribute_repeat_; ReadOnlyScalarAttributeAdapter scalar_attribute_; ReadOnlyScalarAttributeAdapter tex_coord_attribute_[3]; - - // If true, copies OpenGL state automatically - // at each rendering operation. - bool auto_GL_interop_; bool ES_profile_; }; diff --git a/src/lib/geogram_gfx/third_party/ImGui/OLD/README.txt b/src/lib/geogram_gfx/third_party/ImGui/OLD/README.txt index 45388c85..820536f5 100644 --- a/src/lib/geogram_gfx/third_party/ImGui/OLD/README.txt +++ b/src/lib/geogram_gfx/third_party/ImGui/OLD/README.txt @@ -1,6 +1,6 @@ imgui: Bloat-free Immediate Mode Graphical User interface for C++ with minimal dependencies website: https://github.com/ocornut/imgui -This version (1.64) downloaded 09/01/2018 +This version (1.66) downloaded 11/24/2018 My patches/changes indicated by [Bruno Levy] tags Added Android support. \ No newline at end of file diff --git a/src/lib/geogram_gfx/third_party/ImGui/OLD/imconfig.h b/src/lib/geogram_gfx/third_party/ImGui/OLD/imconfig.h index 55ec9650..8d23cbb1 100644 --- a/src/lib/geogram_gfx/third_party/ImGui/OLD/imconfig.h +++ b/src/lib/geogram_gfx/third_party/ImGui/OLD/imconfig.h @@ -20,6 +20,7 @@ #define NULL nullptr #endif +// [Bruno] #define IMGUI_IMPL_OPENGL_LOADER_CUSTOM "glup_compat.h" //---- Define assertion handler. Defaults to calling assert(). @@ -30,6 +31,8 @@ // [Bruno] export ImGUI symbols. #include #define IMGUI_API GEOGRAM_GFX_API +//#define IMGUI_API __declspec( dllexport ) +//#define IMGUI_API __declspec( dllimport ) //---- Don't define obsolete functions/enums names. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names. //#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS @@ -41,6 +44,7 @@ //---- Don't implement some functions to reduce linkage requirements. //#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. //#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] Don't implement default IME handler. Won't use and link with ImmGetContext/ImmSetCompositionWindow. +//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function. //#define IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself if you don't want to link with vsnprintf. //#define IMGUI_DISABLE_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 wrapper so you can implement them yourself. Declare your prototypes in imconfig.h. //#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions(). diff --git a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui.cpp b/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui.cpp index 060560b4..25b71de4 100644 --- a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui.cpp +++ b/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.64 +// dear imgui, v1.66 // (main code and documentation) // Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp for demo code. @@ -22,13 +22,16 @@ Index of this file: DOCUMENTATION + - MISSION STATEMENT - END-USER GUIDE - PROGRAMMER GUIDE (read me!) - Read first - How to update to a newer version of Dear ImGui - Getting started with integrating Dear ImGui in your code/engine - - Using gamepad/keyboard navigation controls [BETA] + - This is how a simple application may look like (2 variations) + - This is how a simple rendering function may look like + - Using gamepad/keyboard navigation controls - API BREAKING CHANGES (read me when you update!) - FREQUENTLY ASKED QUESTIONS (FAQ), TIPS - How can I tell whether to dispatch mouse/keyboard to imgui or to my application? @@ -44,30 +47,35 @@ DOCUMENTATION - I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around.. - How can I help? -CODE -- Forward Declarations -- Context and Memory Allocators -- User facing structures (ImGuiStyle, ImGuiIO) -- Helper/Utilities (ImXXX functions, Color functions) -- ImGuiStorage -- ImGuiTextFilter -- ImGuiTextBuffer -- ImGuiListClipper -- Main Code (most of the code! lots of stuff, needs tidying up) -- Tooltips -- Popups -- Navigation -- Columns -- Drag and Drop -- Logging -- Settings -- Platform Dependent Helpers -- Metrics/Debug window +CODE +(search for "[SECTION]" in the code to find them) + +// [SECTION] FORWARD DECLARATIONS +// [SECTION] CONTEXT AND MEMORY ALLOCATORS +// [SECTION] MAIN USER FACING STRUCTURES (ImGuiStyle, ImGuiIO) +// [SECTION] MISC HELPER/UTILITIES (Maths, String, Format, Hash, File functions) +// [SECTION] MISC HELPER/UTILITIES (ImText* functions) +// [SECTION] MISC HELPER/UTILITIES (Color functions) +// [SECTION] ImGuiStorage +// [SECTION] ImGuiTextFilter +// [SECTION] ImGuiTextBuffer +// [SECTION] ImGuiListClipper +// [SECTION] RENDER HELPERS +// [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!) +// [SECTION] TOOLTIPS +// [SECTION] POPUPS +// [SECTION] KEYBOARD/GAMEPAD NAVIGATION +// [SECTION] COLUMNS +// [SECTION] DRAG AND DROP +// [SECTION] LOGGING/CAPTURING +// [SECTION] SETTINGS +// [SECTION] PLATFORM DEPENDENT HELPERS +// [SECTION] METRICS/DEBUG WINDOW */ //----------------------------------------------------------------------------- -// Documentation +// DOCUMENTATION //----------------------------------------------------------------------------- /* @@ -121,13 +129,15 @@ CODE - Read the FAQ below this section! - Your code creates the UI, if your code doesn't run the UI is gone! The UI can be highly dynamic, there are no construction - or destruction steps, less data retention on your side, less state duplication, less state synchronization, less bugs. + or destruction steps, less superfluous data retention on your side, less state duplication, less state synchronization, less bugs. - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features. - - You can learn about immediate-mode gui principles at http://www.johno.se/book/imgui.html or watch http://mollyrocket.com/861 + - You can learn about immediate-mode GUI principles at http://www.johno.se/book/imgui.html or watch http://mollyrocket.com/861 + See README.md for more links describing the IMGUI paradigm. Dear ImGui is an implementation of the IMGUI paradigm. HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI - Overwrite all the sources files except for imconfig.h (if you have made modification to your copy of imconfig.h) + - Or maintain your own branch where you have imconfig.h modified. - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes. If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will @@ -136,25 +146,25 @@ CODE GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE - - Run and study the examples and demo to get acquainted with the library. + - Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library. - Add the Dear ImGui source files to your projects or using your preferred build system. - It is recommended you build the .cpp files as part of your project and not as a library. - - You can later customize the imconfig.h file to tweak some compilation time behavior, such as integrating imgui types with your own maths types. - - You may be able to grab and copy a ready made imgui_impl_*** file from the examples/ folder. + It is recommended you build and statically link the .cpp files as part of your project and not as shared library (DLL). + - You can later customize the imconfig.h file to tweak some compile-time behavior, such as integrating imgui types with your own maths types. - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them. - Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide. Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render" phases of your own application. All rendering informatioe are stored into command-lists that you will retrieve after calling ImGui::Render(). - Refer to the bindings and demo applications in the examples/ folder for instruction on how to setup your code. + - If you are running over a standard OS with a common graphics API, you should be able to use unmodified imgui_impl_*** files from the examples/ folder. - THIS IS HOW A SIMPLE APPLICATION MAY LOOK LIKE + HOW A SIMPLE APPLICATION MAY LOOK LIKE EXHIBIT 1: USING THE EXAMPLE BINDINGS (imgui_impl_XXX.cpp files from the examples/ folder) // Application init: create a dear imgui context, setup some options, load fonts ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); - // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls - // TODO: Fill optional settings of the io structure later. + // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls. + // TODO: Fill optional fields of the io structure later. // TODO: Load TTF/OTF fonts if you don't want to use the default font. // Initialize helper Platform and Renderer bindings (here we are using imgui_impl_win32 and imgui_impl_dx11) @@ -183,14 +193,14 @@ CODE ImGui_ImplWin32_Shutdown(); ImGui::DestroyContext(); - THIS IS HOW A SIMPLE APPLICATION MAY LOOK LIKE + HOW A SIMPLE APPLICATION MAY LOOK LIKE EXHIBIT 2: IMPLEMENTING CUSTOM BINDING / CUSTOM ENGINE // Application init: create a dear imgui context, setup some options, load fonts ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); - // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls - // TODO: Fill optional settings of the io structure later. + // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls. + // TODO: Fill optional fields of the io structure later. // TODO: Load TTF/OTF fonts if you don't want to use the default font. // Build and load the texture atlas into a texture @@ -238,7 +248,7 @@ CODE // Shutdown ImGui::DestroyContext(); - THIS HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE + HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE void void MyImGuiRenderFunction(ImDrawData* draw_data) { @@ -260,22 +270,22 @@ CODE else { // The texture for the draw call is specified by pcmd->TextureId. - // The vast majority of draw calls with use the imgui texture atlas, which value you have set yourself during initialization. - MyEngineBindTexture(pcmd->TextureId); + // The vast majority of draw calls will use the imgui texture atlas, which value you have set yourself during initialization. + MyEngineBindTexture((MyTexture*)pcmd->TextureId); // We are using scissoring to clip some objects. All low-level graphics API should supports it. // - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches - // (some elements visible outside their bounds) but you can fix that once everywhere else works! + // (some elements visible outside their bounds) but you can fix that once everything else works! // - Clipping coordinates are provided in imgui coordinates space (from draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize) // In a single viewport application, draw_data->DisplayPos will always be (0,0) and draw_data->DisplaySize will always be == io.DisplaySize. - // However, in the interest of supporting multi-viewport applications in the future, always subtract draw_data->DisplayPos from - // clipping bounds to convert them to your viewport space. + // However, in the interest of supporting multi-viewport applications in the future (see 'viewport' branch on github), + // always subtract draw_data->DisplayPos from clipping bounds to convert them to your viewport space. // - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min) ImVec2 pos = draw_data->DisplayPos; MyEngineScissor((int)(pcmd->ClipRect.x - pos.x), (int)(pcmd->ClipRect.y - pos.y), (int)(pcmd->ClipRect.z - pos.x), (int)(pcmd->ClipRect.w - pos.y)); // Render 'pcmd->ElemCount/3' indexed triangles. - // By default the indices ImDrawIdx are 16-bits, you can change them to 32-bits if your engine doesn't support 16-bits indices. + // By default the indices ImDrawIdx are 16-bits, you can change them to 32-bits in imconfig.h if your engine doesn't support 16-bits indices. MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer, vtx_buffer); } idx_buffer += pcmd->ElemCount; @@ -283,15 +293,17 @@ CODE } } - - The examples/ folders contains many functional implementation of the pseudo-code above. + - The examples/ folders contains many actual implementation of the pseudo-codes above. - When calling NewFrame(), the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags are updated. - They tell you if ImGui intends to use your inputs. When a flag is set you want to hide the corresponding inputs from the rest of your application. - In both cases you need to pass on the inputs to imgui. Read the FAQ below for more information about those flags. - - Please read the FAQ above. Amusingly, it is called a FAQ because people frequently have the same issues! + They tell you if Dear ImGui intends to use your inputs. When a flag is set you want to hide the corresponding inputs + from the rest of your application. In every cases you need to pass on the inputs to imgui. Refer to the FAQ for more information. + - Please read the FAQ below!. Amusingly, it is called a FAQ because people frequently run into the same issues! - USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS [BETA] + USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS - - The gamepad/keyboard navigation is in Beta. Ask questions and report issues at https://github.com/ocornut/imgui/issues/787 + - The gamepad/keyboard navigation is fairly functional and keeps being improved. + - Gamepad support is particularly useful to use dear imgui on a console system (e.g. PS4, Switch, XB1) without a mouse! + - You can ask questions and report issues at https://github.com/ocornut/imgui/issues/787 - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable. - Gamepad: - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable. @@ -301,7 +313,7 @@ CODE 0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks. - We uses a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone. Your code will probably need to transform your raw inputs (such as e.g. remapping your 0.2..0.9 raw input range to 0.0..1.0 imgui range, etc.). - - You can download PNG/PSD files depicting the gamepad controls for common controllers at: goo.gl/9LgVZW. + - You can download PNG/PSD files depicting the gamepad controls for common controllers at: http://goo.gl/9LgVZW. - If you need to share inputs between your game and the imgui parts, the easiest approach is to go all-or-nothing, with a buttons combo to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved. - Keyboard: @@ -333,6 +345,11 @@ CODE When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. You can read releases logs https://github.com/ocornut/imgui/releases for more details. + - 2018/10/12 (1.66) - Renamed misc/stl/imgui_stl.* to misc/cpp/imgui_stdlib.* in prevision for other C++ helper files. + - 2018/09/28 (1.66) - renamed SetScrollHere() to SetScrollHereY(). Kept redirection function (will obsolete). + - 2018/09/06 (1.65) - renamed stb_truetype.h to imstb_truetype.h, stb_textedit.h to imstb_textedit.h, and stb_rect_pack.h to imstb_rectpack.h. + If you were conveniently using the imgui copy of those STB headers in your project you will have to update your include paths. + - 2018/09/05 (1.65) - renamed io.OptCursorBlink/io.ConfigCursorBlink to io.ConfigInputTextCursorBlink. (#1427) - 2018/08/31 (1.64) - added imgui_widgets.cpp file, extracted and moved widgets code out of imgui.cpp into imgui_widgets.cpp. Re-ordered some of the code remaining in imgui.cpp. NONE OF THE FUNCTIONS HAVE CHANGED. THE CODE IS SEMANTICALLY 100% IDENTICAL, BUT _EVERY_ FUNCTION HAS BEEN MOVED. Because of this, any local modifications to imgui.cpp will likely conflict when you update. Read docs/CHANGELOG.txt for suggestions. @@ -340,7 +357,7 @@ CODE - 2018/08/21 (1.63) - renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. Kept redirection types (will obsolete). - 2018/08/21 (1.63) - removed ImGuiInputTextCallbackData::ReadOnly since it is a duplication of (ImGuiInputTextCallbackData::Flags & ImGuiInputTextFlags_ReadOnly). - 2018/08/01 (1.63) - removed per-window ImGuiWindowFlags_ResizeFromAnySide beta flag in favor of a global io.ConfigResizeWindowsFromEdges to enable the feature. - - 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink, io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency. + - 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink [-> io.ConfigInputTextCursorBlink in 1.65], io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency. - 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time. - 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete). - 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set. @@ -510,7 +527,7 @@ CODE - When 'io.WantCaptureKeyboard' is set, imgui wants to use your keyboard state, and you may want to discard/hide the inputs from the rest of your application. - When 'io.WantTextInput' is set to may want to notify your OS to popup an on-screen keyboard, if available (e.g. on a mobile phone, or console OS). Note: you should always pass your mouse/keyboard inputs to imgui, even when the io.WantCaptureXXX flag are set false. - This is because imgui needs to detect that you clicked in the void to unfocus its windows. + This is because imgui needs to detect that you clicked in the void to unfocus its own windows. Note: The 'io.WantCaptureMouse' is more accurate that any attempt to "check if the mouse is hovering a window" (don't do that!). It handle mouse dragging correctly (both dragging that started over your application or over an imgui window) and handle e.g. modal windows blocking inputs. Those flags are updated by ImGui::NewFrame(). Preferably read the flags after calling NewFrame() if you can afford it, but reading them before is also @@ -522,7 +539,7 @@ CODE Q: How can I display an image? What is ImTextureID, how does it works? A: Short explanation: - You may use functions such as ImGui::Image(), ImGui::ImageButton() or lower-level ImDrawList::AddImage() to emit draw calls that will use your own textures. - - Actual textures are identified in a way that is up to the user/engine. + - Actual textures are identified in a way that is up to the user/engine. Those identifiers are stored and passed as ImTextureID (void*) value. - Loading image files from the disk and turning them into a texture is not within the scope of Dear ImGui (for a good reason). Please read documentations or tutorials on your graphics API to understand how to display textures on the screen before moving onward. @@ -581,6 +598,8 @@ CODE GLuint my_opengl_texture; glGenTextures(1, &my_opengl_texture); glBindTexture(GL_TEXTURE_2D, my_opengl_texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image_width, image_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data); @@ -647,7 +666,7 @@ CODE - If you want to completely hide the label, but still need an ID: - Checkbox("##On", &b); // Label = "", ID = hash of (..., "##On") // No visible label! + Checkbox("##On", &b); // Label = "", ID = hash of (..., "##On") // No visible label, just a checkbox! - Occasionally/rarely you might want change a label while preserving a constant ID. This allows you to animate labels. For example you may want to include varying information in a window title bar, @@ -657,7 +676,7 @@ CODE Button("World###ID"; // Label = "World", ID = hash of (..., "ID") // Same as above, even though the label looks different sprintf(buf, "My game (%f FPS)###MyGame", fps); - Begin(buf); // Variable label, ID = hash of "MyGame" + Begin(buf); // Variable title, ID = hash of "MyGame" - Solving ID conflict in a more general manner: Use PushID() / PopID() to create scopes and manipulate the ID stack, as to avoid ID conflicts @@ -724,7 +743,8 @@ CODE ImGuiIO& io = ImGui::GetIO(); io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels); io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8() - (default is ProggyClean.ttf, rendered at size 13, embedded in dear imgui's source code) + Default is ProggyClean.ttf, rendered at size 13, embedded in dear imgui's source code. + (Read the 'misc/fonts/README.txt' file for more details about font loading.) New programmers: remember that in C/C++ and most programming languages if you want to use a backslash \ within a string literal, you need to write it double backslash "\\": @@ -734,12 +754,12 @@ CODE Q: How can I easily use icons in my application? A: The most convenient and practical way is to merge an icon font such as FontAwesome inside you - main font. Then you can refer to icons within your strings. Read 'How can I load multiple fonts?' - and the file 'misc/fonts/README.txt' for instructions and useful header files. + main font. Then you can refer to icons within your strings. + (Read the 'misc/fonts/README.txt' file for more details about icons font loading.) Q: How can I load multiple fonts? A: Use the font atlas to pack them into a single texture: - (Read misc/fonts/README.txt and the code in ImFontAtlas for more details.) + (Read the 'misc/fonts/README.txt' file and the code in ImFontAtlas for more details.) ImGuiIO& io = ImGui::GetIO(); ImFont* font0 = io.Fonts->AddFontDefault(); @@ -755,15 +775,15 @@ CODE config.OversampleV = 1; config.GlyphOffset.y -= 2.0f; // Move everything by 2 pixels up config.GlyphExtraSpacing.x = 1.0f; // Increase spacing between characters - io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, &config); + io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_pixels, &config); // Combine multiple fonts into one (e.g. for icon fonts) - ImWchar ranges[] = { 0xf000, 0xf3ff, 0 }; + static ImWchar ranges[] = { 0xf000, 0xf3ff, 0 }; ImFontConfig config; config.MergeMode = true; io.Fonts->AddFontDefault(); - io.Fonts->LoadFromFileTTF("fontawesome-webfont.ttf", 16.0f, &config, ranges); // Merge icon font - io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, NULL, &config, io.Fonts->GetGlyphRangesJapanese()); // Merge japanese glyphs + io.Fonts->AddFontFromFileTTF("fontawesome-webfont.ttf", 16.0f, &config, ranges); // Merge icon font + io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_pixels, NULL, &config, io.Fonts->GetGlyphRangesJapanese()); // Merge japanese glyphs Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic? A: When loading a font, pass custom Unicode ranges to specify the glyphs to load. @@ -793,10 +813,12 @@ CODE the default implementation of io.ImeSetInputScreenPosFn() to set your Microsoft IME position correctly. Q: How can I use the drawing facilities without an ImGui window? (using ImDrawList API) - A: - You can create a dummy window. Call SetNextWindowBgAlpha(0.0f), call Begin() with NoTitleBar|NoResize|NoMove|NoScrollbar|NoSavedSettings|NoInputs flags. + A: - You can create a dummy window. Call Begin() with the NoBackground | NoDecoration | NoSavedSettings | NoInputs flags. + (The ImGuiWindowFlags_NoDecoration flag itself is a shortcut for NoTitleBar | NoResize | NoScrollbar | NoCollapse) Then you can retrieve the ImDrawList* via GetWindowDrawList() and draw to it in any way you like. - You can call ImGui::GetOverlayDrawList() and use this draw list to display contents over every other imgui windows. - - You can create your own ImDrawList instance. You'll need to initialize them ImGui::GetDrawListSharedData(), or create your own ImDrawListSharedData. + - You can create your own ImDrawList instance. You'll need to initialize them ImGui::GetDrawListSharedData(), or create your own ImDrawListSharedData, + and then call your rendered code with your own ImDrawList or ImDrawData data. Q: I integrated Dear ImGui in my engine and the text or lines are blurry.. A: In your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f). @@ -808,7 +830,7 @@ CODE Q: How can I help? A: - If you are experienced with Dear ImGui and C++, look at the github issues, or docs/TODO.txt and see how you want/can help! - - Convince your company to fund development time! Individual users: you can also become a Patron (patreon.com/imgui) or donate on PayPal! See README. + - Convince your company to sponsor/fund development! Individual users: you can also become a Patron (patreon.com/imgui) or donate on PayPal! See README. - Disclose your usage of dear imgui via a dev blog post, a tweet, a screenshot, a mention somewhere etc. You may post screenshot or links in the gallery threads (github.com/ocornut/imgui/issues/1269). Visuals are ideal as they inspire other programmers. But even without visuals, disclosing your use of dear imgui help the library grow credibility, and help other teams and programmers with taking decisions. @@ -842,13 +864,14 @@ CODE #include // intptr_t #endif +// Debug options #define IMGUI_DEBUG_NAV_SCORING 0 #define IMGUI_DEBUG_NAV_RECTS 0 // Visual Studio warnings #ifdef _MSC_VER -#pragma warning (disable: 4127) // condition expression is constant -#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen +#pragma warning (disable: 4127) // condition expression is constant +#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen #endif // Clang/GCC warnings with -Weverything @@ -876,11 +899,15 @@ CODE #endif // When using CTRL+TAB (or Gamepad Square+L/R) we delay the visual a little in order to reduce visual noise doing a fast switch. -static const float NAV_WINDOWING_HIGHLIGHT_DELAY = 0.20f; // Time before the highlight and screen dimming starts fading in -static const float NAV_WINDOWING_LIST_APPEAR_DELAY = 0.15f; // Time before the window list starts to appear +static const float NAV_WINDOWING_HIGHLIGHT_DELAY = 0.20f; // Time before the highlight and screen dimming starts fading in +static const float NAV_WINDOWING_LIST_APPEAR_DELAY = 0.15f; // Time before the window list starts to appear + +// Window resizing from edges (when io.ConfigResizeWindowsFromEdges = true) +static const float RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS = 4.0f; // Extend outside and inside windows. Affect FindHoveredWindow(). +static const float RESIZE_WINDOWS_FROM_EDGES_FEEDBACK_TIMER = 0.04f; // Reduce visual noise by only highlighting the border after a certain time. //------------------------------------------------------------------------- -// Forward Declarations +// [SECTION] FORWARD DECLARATIONS //------------------------------------------------------------------------- static void SetCurrentWindow(ImGuiWindow* window); @@ -894,7 +921,7 @@ static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* wind static void AddDrawListToDrawData(ImVector* out_list, ImDrawList* draw_list); static void AddWindowToDrawData(ImVector* out_list, ImGuiWindow* window); -static void AddWindowToSortedBuffer(ImVector* out_sorted_windows, ImGuiWindow* window); +static void AddWindowToSortBuffer(ImVector* out_sorted_windows, ImGuiWindow* window); static ImRect GetViewportRect(); @@ -912,6 +939,7 @@ namespace ImGui { static bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags); +// Navigation static void NavUpdate(); static void NavUpdateWindowing(); static void NavUpdateWindowingList(); @@ -923,16 +951,25 @@ static ImVec2 NavCalcPreferredRefPos(); static void NavSaveLastChildNavWindow(ImGuiWindow* nav_window); static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window); +// Misc static void UpdateMouseInputs(); static void UpdateMouseWheel(); static void UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4]); } +// Test engine hooks (imgui-test) +//#define IMGUI_ENABLE_TEST_ENGINE_HOOKS +#ifdef IMGUI_ENABLE_TEST_ENGINE_HOOKS +extern void ImGuiTestEngineHook_PreNewFrame(); +extern void ImGuiTestEngineHook_PostNewFrame(); +extern void ImGuiTestEngineHook_ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg); +#endif + //----------------------------------------------------------------------------- -// Context and Memory Allocators +// [SECTION] CONTEXT AND MEMORY ALLOCATORS //----------------------------------------------------------------------------- -// Current context pointer. Implicitly used by all ImGui functions. Always assumed to be != NULL. +// Current context pointer. Implicitly used by all Dear ImGui functions. Always assumed to be != NULL. // CreateContext() will automatically set this pointer if it is NULL. Change to a different context by calling ImGui::SetCurrentContext(). // If you use DLL hotreloading you might need to call SetCurrentContext() after reloading code from this file. // ImGui functions are not thread-safe because of this pointer. If you want thread-safety to allow N threads to access N different contexts, you can: @@ -958,7 +995,7 @@ static void (*GImAllocatorFreeFunc)(void* ptr, void* user_data) = FreeWrapper; static void* GImAllocatorUserData = NULL; //----------------------------------------------------------------------------- -// User facing main structures +// [SECTION] MAIN USER FACING STRUCTURES (ImGuiStyle, ImGuiIO) //----------------------------------------------------------------------------- ImGuiStyle::ImGuiStyle() @@ -986,7 +1023,7 @@ ImGuiStyle::ImGuiStyle() GrabMinSize = 10.0f; // Minimum width/height of a grab box for slider/scrollbar GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs. ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text. - DisplayWindowPadding = ImVec2(20,20); // Window positions are clamped to be visible within the display area by at least this amount. Only covers regular windows. + DisplayWindowPadding = ImVec2(20,20); // Window position are clamped to be visible within the display area by at least this amount. Only applies to regular windows. DisplaySafeAreaPadding = ImVec2(3,3); // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows. MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later. AntiAliasedLines = true; // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU. @@ -1056,7 +1093,7 @@ ImGuiIO::ImGuiIO() #else ConfigMacOSXBehaviors = false; #endif - ConfigCursorBlink = true; + ConfigInputTextCursorBlink = true; ConfigResizeWindowsFromEdges = false; // Settings (User Functions) @@ -1103,7 +1140,7 @@ void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars) } //----------------------------------------------------------------------------- -// HELPERS/UTILITIES +// [SECTION] MISC HELPER/UTILITIES (Maths, String, Format, Hash, File functions) //----------------------------------------------------------------------------- ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p) @@ -1184,19 +1221,25 @@ char* ImStrdup(const char *str) const char* ImStrchrRange(const char* str, const char* str_end, char c) { - for ( ; str < str_end; str++) - if (*str == c) - return str; - return NULL; + const char* p = (const char*)memchr(str, (int)c, str_end - str); + return p; } int ImStrlenW(const ImWchar* str) { + //return (int)wcslen((const wchar_t*)str); // FIXME-OPT: Could use this when wchar_t are 16-bits int n = 0; while (*str++) n++; return n; } +// Find end-of-line. Return pointer will point to either first \n, either str_end. +const char* ImStreolRange(const char* str, const char* str_end) +{ + const char* p = (const char*)memchr(str, '\n', str_end - str); + return p ? p : str_end; +} + const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line { while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n') @@ -1321,8 +1364,66 @@ ImU32 ImHash(const void* data, int data_size, ImU32 seed) return ~crc; } +FILE* ImFileOpen(const char* filename, const char* mode) +{ +#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__GNUC__) + // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames. Converting both strings from UTF-8 to wchar format (using a single allocation, because we can) + const int filename_wsize = ImTextCountCharsFromUtf8(filename, NULL) + 1; + const int mode_wsize = ImTextCountCharsFromUtf8(mode, NULL) + 1; + ImVector buf; + buf.resize(filename_wsize + mode_wsize); + ImTextStrFromUtf8(&buf[0], filename_wsize, filename, NULL); + ImTextStrFromUtf8(&buf[filename_wsize], mode_wsize, mode, NULL); + return _wfopen((wchar_t*)&buf[0], (wchar_t*)&buf[filename_wsize]); +#else + return fopen(filename, mode); +#endif +} + +// Load file content into memory +// Memory allocated with ImGui::MemAlloc(), must be freed by user using ImGui::MemFree() +void* ImFileLoadToMemory(const char* filename, const char* file_open_mode, size_t* out_file_size, int padding_bytes) +{ + IM_ASSERT(filename && file_open_mode); + if (out_file_size) + *out_file_size = 0; + + FILE* f; + if ((f = ImFileOpen(filename, file_open_mode)) == NULL) + return NULL; + + long file_size_signed; + if (fseek(f, 0, SEEK_END) || (file_size_signed = ftell(f)) == -1 || fseek(f, 0, SEEK_SET)) + { + fclose(f); + return NULL; + } + + size_t file_size = (size_t)file_size_signed; + void* file_data = ImGui::MemAlloc(file_size + padding_bytes); + if (file_data == NULL) + { + fclose(f); + return NULL; + } + if (fread(file_data, 1, file_size, f) != file_size) + { + fclose(f); + ImGui::MemFree(file_data); + return NULL; + } + if (padding_bytes > 0) + memset((void*)(((char*)file_data) + file_size), 0, (size_t)padding_bytes); + + fclose(f); + if (out_file_size) + *out_file_size = file_size; + + return file_data; +} + //----------------------------------------------------------------------------- -// HELPERS/UTILITIES (ImText* helpers) +// [SECTION] MISC HELPERS/UTILITIES (ImText* functions) //----------------------------------------------------------------------------- // Convert UTF-8 to 32-bits character, process single character input. @@ -1504,66 +1605,9 @@ int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_e return bytes_count; } -FILE* ImFileOpen(const char* filename, const char* mode) -{ -#if defined(_WIN32) && !defined(__CYGWIN__) - // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames. Converting both strings from UTF-8 to wchar format (using a single allocation, because we can) - const int filename_wsize = ImTextCountCharsFromUtf8(filename, NULL) + 1; - const int mode_wsize = ImTextCountCharsFromUtf8(mode, NULL) + 1; - ImVector buf; - buf.resize(filename_wsize + mode_wsize); - ImTextStrFromUtf8(&buf[0], filename_wsize, filename, NULL); - ImTextStrFromUtf8(&buf[filename_wsize], mode_wsize, mode, NULL); - return _wfopen((wchar_t*)&buf[0], (wchar_t*)&buf[filename_wsize]); -#else - return fopen(filename, mode); -#endif -} - -// Load file content into memory -// Memory allocated with ImGui::MemAlloc(), must be freed by user using ImGui::MemFree() -void* ImFileLoadToMemory(const char* filename, const char* file_open_mode, size_t* out_file_size, int padding_bytes) -{ - IM_ASSERT(filename && file_open_mode); - if (out_file_size) - *out_file_size = 0; - - FILE* f; - if ((f = ImFileOpen(filename, file_open_mode)) == NULL) - return NULL; - - long file_size_signed; - if (fseek(f, 0, SEEK_END) || (file_size_signed = ftell(f)) == -1 || fseek(f, 0, SEEK_SET)) - { - fclose(f); - return NULL; - } - - size_t file_size = (size_t)file_size_signed; - void* file_data = ImGui::MemAlloc(file_size + padding_bytes); - if (file_data == NULL) - { - fclose(f); - return NULL; - } - if (fread(file_data, 1, file_size, f) != file_size) - { - fclose(f); - ImGui::MemFree(file_data); - return NULL; - } - if (padding_bytes > 0) - memset((void*)(((char*)file_data) + file_size), 0, (size_t)padding_bytes); - - fclose(f); - if (out_file_size) - *out_file_size = file_size; - - return file_data; -} - //----------------------------------------------------------------------------- -// COLOR FUNCTIONS +// [SECTION] MISC HELPER/UTILTIES (Color functions) +// Note: The Convert functions are early design which are not consistent with other API. //----------------------------------------------------------------------------- ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in) @@ -1670,7 +1714,7 @@ ImU32 ImGui::GetColorU32(ImU32 col) } //----------------------------------------------------------------------------- -// ImGuiStorage +// [SECTION] ImGuiStorage // Helper: Key->value storage //----------------------------------------------------------------------------- @@ -1819,7 +1863,7 @@ void ImGuiStorage::SetAllInt(int v) } //----------------------------------------------------------------------------- -// ImGuiTextFilter +// [SECTION] ImGuiTextFilter //----------------------------------------------------------------------------- // Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]" @@ -1923,7 +1967,7 @@ bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const } //----------------------------------------------------------------------------- -// ImGuiTextBuffer +// [SECTION] ImGuiTextBuffer //----------------------------------------------------------------------------- // On some platform vsnprintf() takes va_list by reference and modifies it. @@ -1936,6 +1980,8 @@ bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const #endif #endif +char ImGuiTextBuffer::EmptyString[1] = { 0 }; + // Helper: Text buffer for logging/accumulating text void ImGuiTextBuffer::appendfv(const char* fmt, va_list args) { @@ -1949,7 +1995,8 @@ void ImGuiTextBuffer::appendfv(const char* fmt, va_list args) return; } - const int write_off = Buf.Size; + // Add zero-terminator the first time + const int write_off = (Buf.Size != 0) ? Buf.Size : 1; const int needed_sz = write_off + len; if (write_off + len >= Buf.Capacity) { @@ -1971,17 +2018,18 @@ void ImGuiTextBuffer::appendf(const char* fmt, ...) } //----------------------------------------------------------------------------- -// ImGuiListClipper +// [SECTION] ImGuiListClipper +// This is currently not as flexible/powerful as it should be, needs some rework (see TODO) //----------------------------------------------------------------------------- static void SetCursorPosYAndSetupDummyPrevLine(float pos_y, float line_height) { - // Set cursor position and a few other things so that SetScrollHere() and Columns() can work when seeking cursor. + // Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor. // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue. // The clipper should probably have a 4th step to display the last item in a regular manner. ImGui::SetCursorPosY(pos_y); ImGuiWindow* window = ImGui::GetCurrentWindow(); - window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y - line_height; // Setting those fields so that SetScrollHere() can properly function after the end of our clipper usage. + window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y - line_height; // Setting those fields so that SetScrollHereY() can properly function after the end of our clipper usage. window->DC.PrevLineSize.y = (line_height - GImGui->Style.ItemSpacing.y); // If we end up needing more accurate data (to e.g. use SameLine) we may as well make the clipper have a fourth step to let user process and display the last item in their list. if (window->DC.ColumnsSet) window->DC.ColumnsSet->LineMinY = window->DC.CursorPos.y; // Setting this so that cell Y position are set properly @@ -2055,112 +2103,331 @@ bool ImGuiListClipper::Step() } //----------------------------------------------------------------------------- -// MAIN CODE -// (this category is still too large and badly ordered, needs some tidying up) +// [SECTION] RENDER HELPERS +// Those (internal) functions are currently quite a legacy mess - their signature and behavior will change. +// Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: state. //----------------------------------------------------------------------------- -// ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods -ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) - : DrawListInst(&context->DrawListSharedData) +const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end) { - Name = ImStrdup(name); - ID = ImHash(name, 0); - IDStack.push_back(ID); - Flags = 0; - Pos = ImVec2(0.0f, 0.0f); - Size = SizeFull = ImVec2(0.0f, 0.0f); - SizeContents = SizeContentsExplicit = ImVec2(0.0f, 0.0f); - WindowPadding = ImVec2(0.0f, 0.0f); - WindowRounding = 0.0f; - WindowBorderSize = 0.0f; - MoveId = GetID("#MOVE"); - ChildId = 0; - Scroll = ImVec2(0.0f, 0.0f); - ScrollTarget = ImVec2(FLT_MAX, FLT_MAX); - ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f); - ScrollbarSizes = ImVec2(0.0f, 0.0f); - ScrollbarX = ScrollbarY = false; - Active = WasActive = false; - WriteAccessed = false; - Collapsed = false; - WantCollapseToggle = false; - SkipItems = false; - Appearing = false; - Hidden = false; - HasCloseButton = false; - BeginOrderWithinParent = -1; - BeginOrderWithinContext = -1; - BeginCount = 0; - PopupId = 0; - AutoFitFramesX = AutoFitFramesY = -1; - AutoFitOnlyGrows = false; - AutoFitChildAxises = 0x00; - AutoPosLastDirection = ImGuiDir_None; - HiddenFramesRegular = HiddenFramesForResize = 0; - SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing; - SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX); - - LastFrameActive = -1; - ItemWidthDefault = 0.0f; - FontWindowScale = 1.0f; - SettingsIdx = -1; - - DrawList = &DrawListInst; - DrawList->_OwnerName = Name; - ParentWindow = NULL; - RootWindow = NULL; - RootWindowForTitleBarHighlight = NULL; - RootWindowForNav = NULL; - - NavLastIds[0] = NavLastIds[1] = 0; - NavRectRel[0] = NavRectRel[1] = ImRect(); - NavLastChildNavWindow = NULL; + const char* text_display_end = text; + if (!text_end) + text_end = (const char*)-1; - FocusIdxAllCounter = FocusIdxTabCounter = -1; - FocusIdxAllRequestCurrent = FocusIdxTabRequestCurrent = INT_MAX; - FocusIdxAllRequestNext = FocusIdxTabRequestNext = INT_MAX; + while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#')) + text_display_end++; + return text_display_end; } -ImGuiWindow::~ImGuiWindow() +// Internal ImGui functions to render text +// RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText() +void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash) { - IM_ASSERT(DrawList == &DrawListInst); - IM_DELETE(Name); - for (int i = 0; i != ColumnsStorage.Size; i++) - ColumnsStorage[i].~ImGuiColumnsSet(); -} + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; -ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end) -{ - ImGuiID seed = IDStack.back(); - ImGuiID id = ImHash(str, str_end ? (int)(str_end - str) : 0, seed); - ImGui::KeepAliveID(id); - return id; -} + // Hide anything after a '##' string + const char* text_display_end; + if (hide_text_after_hash) + { + text_display_end = FindRenderedTextEnd(text, text_end); + } + else + { + if (!text_end) + text_end = text + strlen(text); // FIXME-OPT + text_display_end = text_end; + } -ImGuiID ImGuiWindow::GetID(const void* ptr) -{ - ImGuiID seed = IDStack.back(); - ImGuiID id = ImHash(&ptr, sizeof(void*), seed); - ImGui::KeepAliveID(id); - return id; + if (text != text_display_end) + { + window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end); + if (g.LogEnabled) + LogRenderedText(&pos, text, text_display_end); + } } -ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end) +void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width) { - ImGuiID seed = IDStack.back(); - return ImHash(str, str_end ? (int)(str_end - str) : 0, seed); -} + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; -ImGuiID ImGuiWindow::GetIDNoKeepAlive(const void* ptr) -{ - ImGuiID seed = IDStack.back(); - return ImHash(&ptr, sizeof(void*), seed); + if (!text_end) + text_end = text + strlen(text); // FIXME-OPT + + if (text != text_end) + { + window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width); + if (g.LogEnabled) + LogRenderedText(&pos, text, text_end); + } } -// This is only used in rare/specific situations to manufacture an ID out of nowhere. -ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs) +// Default clip_rect uses (pos_min,pos_max) +// Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges) +void ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect) { - ImGuiID seed = IDStack.back(); + // Hide anything after a '##' string + const char* text_display_end = FindRenderedTextEnd(text, text_end); + const int text_len = (int)(text_display_end - text); + if (text_len == 0) + return; + + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + // Perform CPU side clipping for single clipped element to avoid using scissor state + ImVec2 pos = pos_min; + const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f); + + const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min; + const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max; + bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y); + if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min + need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y); + + // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment. + if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x); + if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y); + + // Render + if (need_clipping) + { + ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y); + window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect); + } + else + { + window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL); + } + if (g.LogEnabled) + LogRenderedText(&pos, text, text_display_end); +} + +// Render a rectangle shaped with optional rounding and borders +void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding); + const float border_size = g.Style.FrameBorderSize; + if (border && border_size > 0.0f) + { + window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size); + window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size); + } +} + +void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + const float border_size = g.Style.FrameBorderSize; + if (border_size > 0.0f) + { + window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size); + window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size); + } +} + +// Render an arrow aimed to be aligned with text (p_min is a position in the same space text would be positioned). To e.g. denote expanded/collapsed state +void ImGui::RenderArrow(ImVec2 p_min, ImGuiDir dir, float scale) +{ + ImGuiContext& g = *GImGui; + + const float h = g.FontSize * 1.00f; + float r = h * 0.40f * scale; + ImVec2 center = p_min + ImVec2(h * 0.50f, h * 0.50f * scale); + + ImVec2 a, b, c; + switch (dir) + { + case ImGuiDir_Up: + case ImGuiDir_Down: + if (dir == ImGuiDir_Up) r = -r; + a = ImVec2(+0.000f,+0.750f) * r; + b = ImVec2(-0.866f,-0.750f) * r; + c = ImVec2(+0.866f,-0.750f) * r; + break; + case ImGuiDir_Left: + case ImGuiDir_Right: + if (dir == ImGuiDir_Left) r = -r; + a = ImVec2(+0.750f,+0.000f) * r; + b = ImVec2(-0.750f,+0.866f) * r; + c = ImVec2(-0.750f,-0.866f) * r; + break; + case ImGuiDir_None: + case ImGuiDir_COUNT: + IM_ASSERT(0); + break; + } + + g.CurrentWindow->DrawList->AddTriangleFilled(center + a, center + b, center + c, GetColorU32(ImGuiCol_Text)); +} + +void ImGui::RenderBullet(ImVec2 pos) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + window->DrawList->AddCircleFilled(pos, g.FontSize*0.20f, GetColorU32(ImGuiCol_Text), 8); +} + +void ImGui::RenderCheckMark(ImVec2 pos, ImU32 col, float sz) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + float thickness = ImMax(sz / 5.0f, 1.0f); + sz -= thickness*0.5f; + pos += ImVec2(thickness*0.25f, thickness*0.25f); + + float third = sz / 3.0f; + float bx = pos.x + third; + float by = pos.y + sz - third*0.5f; + window->DrawList->PathLineTo(ImVec2(bx - third, by - third)); + window->DrawList->PathLineTo(ImVec2(bx, by)); + window->DrawList->PathLineTo(ImVec2(bx + third*2, by - third*2)); + window->DrawList->PathStroke(col, false, thickness); +} + +void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags) +{ + ImGuiContext& g = *GImGui; + if (id != g.NavId) + return; + if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw)) + return; + ImGuiWindow* window = g.CurrentWindow; + if (window->DC.NavHideHighlightOneFrame) + return; + + float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding; + ImRect display_rect = bb; + display_rect.ClipWith(window->ClipRect); + if (flags & ImGuiNavHighlightFlags_TypeDefault) + { + const float THICKNESS = 2.0f; + const float DISTANCE = 3.0f + THICKNESS * 0.5f; + display_rect.Expand(ImVec2(DISTANCE,DISTANCE)); + bool fully_visible = window->ClipRect.Contains(display_rect); + if (!fully_visible) + window->DrawList->PushClipRect(display_rect.Min, display_rect.Max); + window->DrawList->AddRect(display_rect.Min + ImVec2(THICKNESS*0.5f,THICKNESS*0.5f), display_rect.Max - ImVec2(THICKNESS*0.5f,THICKNESS*0.5f), GetColorU32(ImGuiCol_NavHighlight), rounding, ImDrawCornerFlags_All, THICKNESS); + if (!fully_visible) + window->DrawList->PopClipRect(); + } + if (flags & ImGuiNavHighlightFlags_TypeThin) + { + window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, ~0, 1.0f); + } +} + +//----------------------------------------------------------------------------- +// [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!) +//----------------------------------------------------------------------------- + +// ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods +ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) + : DrawListInst(&context->DrawListSharedData) +{ + Name = ImStrdup(name); + ID = ImHash(name, 0); + IDStack.push_back(ID); + Flags = 0; + Pos = ImVec2(0.0f, 0.0f); + Size = SizeFull = ImVec2(0.0f, 0.0f); + SizeContents = SizeContentsExplicit = ImVec2(0.0f, 0.0f); + WindowPadding = ImVec2(0.0f, 0.0f); + WindowRounding = 0.0f; + WindowBorderSize = 0.0f; + MoveId = GetID("#MOVE"); + ChildId = 0; + Scroll = ImVec2(0.0f, 0.0f); + ScrollTarget = ImVec2(FLT_MAX, FLT_MAX); + ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f); + ScrollbarSizes = ImVec2(0.0f, 0.0f); + ScrollbarX = ScrollbarY = false; + Active = WasActive = false; + WriteAccessed = false; + Collapsed = false; + WantCollapseToggle = false; + SkipItems = false; + Appearing = false; + Hidden = false; + HasCloseButton = false; + BeginCount = 0; + BeginOrderWithinParent = -1; + BeginOrderWithinContext = -1; + PopupId = 0; + AutoFitFramesX = AutoFitFramesY = -1; + AutoFitOnlyGrows = false; + AutoFitChildAxises = 0x00; + AutoPosLastDirection = ImGuiDir_None; + HiddenFramesRegular = HiddenFramesForResize = 0; + SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing; + SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX); + + LastFrameActive = -1; + ItemWidthDefault = 0.0f; + FontWindowScale = 1.0f; + SettingsIdx = -1; + + DrawList = &DrawListInst; + DrawList->_OwnerName = Name; + ParentWindow = NULL; + RootWindow = NULL; + RootWindowForTitleBarHighlight = NULL; + RootWindowForNav = NULL; + + NavLastIds[0] = NavLastIds[1] = 0; + NavRectRel[0] = NavRectRel[1] = ImRect(); + NavLastChildNavWindow = NULL; + + FocusIdxAllCounter = FocusIdxTabCounter = -1; + FocusIdxAllRequestCurrent = FocusIdxTabRequestCurrent = INT_MAX; + FocusIdxAllRequestNext = FocusIdxTabRequestNext = INT_MAX; +} + +ImGuiWindow::~ImGuiWindow() +{ + IM_ASSERT(DrawList == &DrawListInst); + IM_DELETE(Name); + for (int i = 0; i != ColumnsStorage.Size; i++) + ColumnsStorage[i].~ImGuiColumnsSet(); +} + +ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end) +{ + ImGuiID seed = IDStack.back(); + ImGuiID id = ImHash(str, str_end ? (int)(str_end - str) : 0, seed); + ImGui::KeepAliveID(id); + return id; +} + +ImGuiID ImGuiWindow::GetID(const void* ptr) +{ + ImGuiID seed = IDStack.back(); + ImGuiID id = ImHash(&ptr, sizeof(void*), seed); + ImGui::KeepAliveID(id); + return id; +} + +ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end) +{ + ImGuiID seed = IDStack.back(); + return ImHash(str, str_end ? (int)(str_end - str) : 0, seed); +} + +ImGuiID ImGuiWindow::GetIDNoKeepAlive(const void* ptr) +{ + ImGuiID seed = IDStack.back(); + return ImHash(&ptr, sizeof(void*), seed); +} + +// This is only used in rare/specific situations to manufacture an ID out of nowhere. +ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs) +{ + ImGuiID seed = IDStack.back(); const int r_rel[4] = { (int)(r_abs.Min.x - Pos.x), (int)(r_abs.Min.y - Pos.y), (int)(r_abs.Max.x - Pos.x), (int)(r_abs.Max.y - Pos.y) }; ImGuiID id = ImHash(&r_rel, sizeof(r_rel), seed); ImGui::KeepAliveID(id); @@ -2252,7 +2519,7 @@ void ImGui::SetHoveredID(ImGuiID id) g.HoveredId = id; g.HoveredIdAllowOverlap = false; if (id != 0 && g.HoveredIdPreviousFrame != id) - g.HoveredIdTimer = 0.0f; + g.HoveredIdTimer = g.HoveredIdNotActiveTimer = 0.0f; } ImGuiID ImGui::GetHoveredID() @@ -2339,6 +2606,10 @@ void ImGui::ItemSize(const ImRect& bb, float text_offset_y) // declare their minimum size requirement to ItemSize() and then use a larger region for drawing/interaction, which is passed to ItemAdd(). bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg) { +#ifdef IMGUI_ENABLE_TEST_ENGINE_HOOKS + ImGuiTestEngineHook_ItemAdd(bb, id, nav_bb_arg); +#endif + ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; @@ -2452,19 +2723,19 @@ bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id, bool tab_stop { ImGuiContext& g = *GImGui; - const bool allow_keyboard_focus = (window->DC.ItemFlags & (ImGuiItemFlags_AllowKeyboardFocus | ImGuiItemFlags_Disabled)) == ImGuiItemFlags_AllowKeyboardFocus; + const bool is_tab_stop = (window->DC.ItemFlags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0; window->FocusIdxAllCounter++; - if (allow_keyboard_focus) + if (is_tab_stop) window->FocusIdxTabCounter++; // Process keyboard input at this point: TAB/Shift-TAB to tab out of the currently focused item. // Note that we can always TAB out of a widget that doesn't allow tabbing in. if (tab_stop && (g.ActiveId == id) && window->FocusIdxAllRequestNext == INT_MAX && window->FocusIdxTabRequestNext == INT_MAX && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab)) - window->FocusIdxTabRequestNext = window->FocusIdxTabCounter + (g.IO.KeyShift ? (allow_keyboard_focus ? -1 : 0) : +1); // Modulo on index will be applied at the end of frame once we've got the total counter of items. + window->FocusIdxTabRequestNext = window->FocusIdxTabCounter + (g.IO.KeyShift ? (is_tab_stop ? -1 : 0) : +1); // Modulo on index will be applied at the end of frame once we've got the total counter of items. if (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent) return true; - if (allow_keyboard_focus && window->FocusIdxTabCounter == window->FocusIdxTabRequestCurrent) + if (is_tab_stop && window->FocusIdxTabCounter == window->FocusIdxTabRequestCurrent) { g.NavJustTabbedId = id; return true; @@ -2506,8 +2777,6 @@ float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x) return ImMax(wrap_pos_x - pos.x, 1.0f); } -//----------------------------------------------------------------------------- - void* ImGui::MemAlloc(size_t size) { if (ImGuiContext* ctx = GImGui) @@ -2624,6 +2893,12 @@ int ImGui::GetFrameCount() return GImGui->FrameCount; } +static ImDrawList* GetOverlayDrawList(ImGuiWindow*) +{ + // This seemingly unnecessary wrapper simplifies compatibility between the 'master' and 'viewport' branches. + return &GImGui->OverlayDrawList; +} + ImDrawList* ImGui::GetOverlayDrawList() { return &GImGui->OverlayDrawList; @@ -2695,7 +2970,11 @@ static void ImGui::UpdateMouseInputs() { ImGuiContext& g = *GImGui; - // If mouse just appeared or disappeared (usually denoted by -FLT_MAX component, but in reality we test for -256000.0f) we cancel out movement in MouseDelta + // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well) + if (IsMousePosValid(&g.IO.MousePos)) + g.IO.MousePos = g.LastValidMousePos = ImFloor(g.IO.MousePos); + + // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev)) g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev; else @@ -2752,9 +3031,9 @@ void ImGui::UpdateMouseWheel() // If a child window has the ImGuiWindowFlags_NoScrollWithMouse flag, we give a chance to scroll its parent (unless either ImGuiWindowFlags_NoInputs or ImGuiWindowFlags_NoScrollbar are also set). ImGuiWindow* window = g.HoveredWindow; ImGuiWindow* scroll_window = window; - while ((scroll_window->Flags & ImGuiWindowFlags_ChildWindow) && (scroll_window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(scroll_window->Flags & ImGuiWindowFlags_NoScrollbar) && !(scroll_window->Flags & ImGuiWindowFlags_NoInputs) && scroll_window->ParentWindow) + while ((scroll_window->Flags & ImGuiWindowFlags_ChildWindow) && (scroll_window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(scroll_window->Flags & ImGuiWindowFlags_NoScrollbar) && !(scroll_window->Flags & ImGuiWindowFlags_NoMouseInputs) && scroll_window->ParentWindow) scroll_window = scroll_window->ParentWindow; - const bool scroll_allowed = !(scroll_window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(scroll_window->Flags & ImGuiWindowFlags_NoInputs); + const bool scroll_allowed = !(scroll_window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(scroll_window->Flags & ImGuiWindowFlags_NoMouseInputs); if (g.IO.MouseWheel != 0.0f) { @@ -2850,6 +3129,10 @@ void ImGui::NewFrame() IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?"); ImGuiContext& g = *GImGui; +#ifdef IMGUI_ENABLE_TEST_ENGINE_HOOKS + ImGuiTestEngineHook_PreNewFrame(); +#endif + // Check user data // (We pass an error message in the assert expression to make it visible to programmers who are not using a debugger, as most assert handlers display their argument) IM_ASSERT(g.Initialized); @@ -2863,11 +3146,11 @@ void ImGui::NewFrame() for (int n = 0; n < ImGuiKey_COUNT; n++) IM_ASSERT(g.IO.KeyMap[n] >= -1 && g.IO.KeyMap[n] < IM_ARRAYSIZE(g.IO.KeysDown) && "io.KeyMap[] contains an out of bound value (need to be 0..512, or -1 for unmapped key)"); - // Perform simple check for required key mapping (we intentionally do NOT check all keys to not pressure user into setting up everything, but Space is required and was only recently added in 1.60 WIP) + // Perform simple check: required key mapping (we intentionally do NOT check all keys to not pressure user into setting up everything, but Space is required and was only recently added in 1.60 WIP) if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation."); - // The beta io.ConfigResizeWindowsFromEdges option requires back-end to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly. + // Perform simple check: the beta io.ConfigResizeWindowsFromEdges option requires back-end to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly. if (g.IO.ConfigResizeWindowsFromEdges && !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseCursors)) g.IO.ConfigResizeWindowsFromEdges = false; @@ -2922,8 +3205,12 @@ void ImGui::NewFrame() // Clear reference to active widget if the widget isn't alive anymore if (!g.HoveredIdPreviousFrame) g.HoveredIdTimer = 0.0f; + if (!g.HoveredIdPreviousFrame || (g.HoveredId && g.ActiveId == g.HoveredId)) + g.HoveredIdNotActiveTimer = 0.0f; if (g.HoveredId) g.HoveredIdTimer += g.IO.DeltaTime; + if (g.HoveredId && g.ActiveId != g.HoveredId) + g.HoveredIdNotActiveTimer += g.IO.DeltaTime; g.HoveredIdPreviousFrame = g.HoveredId; g.HoveredId = 0; g.HoveredIdAllowOverlap = false; @@ -2992,6 +3279,7 @@ void ImGui::NewFrame() g.NavIdTabCounter = INT_MAX; // Mark all windows as not visible + IM_ASSERT(g.WindowsFocusOrder.Size == g.Windows.Size); for (int i = 0; i != g.Windows.Size; i++) { ImGuiWindow* window = g.Windows[i]; @@ -3002,7 +3290,7 @@ void ImGui::NewFrame() // Closing the focused window restore focus to the first active root window in descending z-order if (g.NavWindow && !g.NavWindow->WasActive) - FocusFrontMostActiveWindowIgnoringOne(NULL); + FocusPreviousWindowIgnoringOne(NULL); // No window should be open at the beginning of the frame. // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear. @@ -3014,6 +3302,10 @@ void ImGui::NewFrame() // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags. SetNextWindowSize(ImVec2(400,400), ImGuiCond_FirstUseEver); Begin("Debug##Default"); + +#ifdef IMGUI_ENABLE_TEST_ENGINE_HOOKS + ImGuiTestEngineHook_PostNewFrame(); +#endif } void ImGui::Initialize(ImGuiContext* context) @@ -3039,7 +3331,10 @@ void ImGui::Shutdown(ImGuiContext* context) // The fonts atlas can be used prior to calling NewFrame(), so we clear it even if g.Initialized is FALSE (which would happen if we never called NewFrame) ImGuiContext& g = *context; if (g.IO.Fonts && g.FontAtlasOwnedByContext) + { + g.IO.Fonts->Locked = false; IM_DELETE(g.IO.Fonts); + } g.IO.Fonts = NULL; // Cleanup of other data are conditional on actually having initialized ImGui. @@ -3048,12 +3343,18 @@ void ImGui::Shutdown(ImGuiContext* context) // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file) if (g.SettingsLoaded && g.IO.IniFilename != NULL) + { + ImGuiContext* backup_context = GImGui; + SetCurrentContext(context); SaveIniSettingsToDisk(g.IO.IniFilename); + SetCurrentContext(backup_context); + } // Clear everything else for (int i = 0; i < g.Windows.Size; i++) IM_DELETE(g.Windows[i]); g.Windows.clear(); + g.WindowsFocusOrder.clear(); g.WindowsSortBuffer.clear(); g.CurrentWindow = NULL; g.CurrentWindowStack.clear(); @@ -3102,7 +3403,7 @@ static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs) return (a->BeginOrderWithinParent - b->BeginOrderWithinParent); } -static void AddWindowToSortedBuffer(ImVector* out_sorted_windows, ImGuiWindow* window) +static void AddWindowToSortBuffer(ImVector* out_sorted_windows, ImGuiWindow* window) { out_sorted_windows->push_back(window); if (window->Active) @@ -3114,7 +3415,7 @@ static void AddWindowToSortedBuffer(ImVector* out_sorted_windows, { ImGuiWindow* child = window->DC.ChildWindows[i]; if (child->Active) - AddWindowToSortedBuffer(out_sorted_windows, child); + AddWindowToSortBuffer(out_sorted_windows, child); } } } @@ -3192,19 +3493,19 @@ void ImDrawDataBuilder::FlattenIntoSingleLayer() } } -static void SetupDrawData(ImVector* draw_lists, ImDrawData* out_draw_data) +static void SetupDrawData(ImVector* draw_lists, ImDrawData* draw_data) { ImGuiIO& io = ImGui::GetIO(); - out_draw_data->Valid = true; - out_draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL; - out_draw_data->CmdListsCount = draw_lists->Size; - out_draw_data->TotalVtxCount = out_draw_data->TotalIdxCount = 0; - out_draw_data->DisplayPos = ImVec2(0.0f, 0.0f); - out_draw_data->DisplaySize = io.DisplaySize; + draw_data->Valid = true; + draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL; + draw_data->CmdListsCount = draw_lists->Size; + draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0; + draw_data->DisplayPos = ImVec2(0.0f, 0.0f); + draw_data->DisplaySize = io.DisplaySize; for (int n = 0; n < draw_lists->Size; n++) { - out_draw_data->TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size; - out_draw_data->TotalIdxCount += draw_lists->Data[n]->IdxBuffer.Size; + draw_data->TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size; + draw_data->TotalIdxCount += draw_lists->Data[n]->IdxBuffer.Size; } } @@ -3235,8 +3536,9 @@ void ImGui::EndFrame() // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME) if ( g.FrameCountEnded == -1 || // [Ben Li & Bruno Levy], avoids an FPE because PlatformImeLastPos is not initialized - (g.IO.ImeSetInputScreenPosFn && ImLengthSqr(g.PlatformImeLastPos - g.PlatformImePos) > 0.0001f) - ) { + (g.IO.ImeSetInputScreenPosFn && ImLengthSqr(g.PlatformImeLastPos - g.PlatformImePos) > 0.0001f) + ) + { g.IO.ImeSetInputScreenPosFn((int)g.PlatformImePos.x, (int)g.PlatformImePos.y); g.PlatformImeLastPos = g.PlatformImePos; } @@ -3314,7 +3616,7 @@ void ImGui::EndFrame() ImGuiWindow* window = g.Windows[i]; if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow)) // if a child is active its parent will add it continue; - AddWindowToSortedBuffer(&g.WindowsSortBuffer, window); + AddWindowToSortBuffer(&g.WindowsSortBuffer, window); } IM_ASSERT(g.Windows.Size == g.WindowsSortBuffer.Size); // we done something wrong @@ -3339,7 +3641,7 @@ void ImGui::Render() IM_ASSERT(g.Initialized); if (g.FrameCountEnded != g.FrameCount) - ImGui::EndFrame(); + EndFrame(); g.FrameCountRendered = g.FrameCount; // Gather ImDrawList to render (for each active window) @@ -3356,240 +3658,26 @@ void ImGui::Render() } for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_front_most); n++) if (windows_to_render_front_most[n] && IsWindowActiveAndVisible(windows_to_render_front_most[n])) // NavWindowingTarget is always temporarily displayed as the front-most window - AddWindowToDrawDataSelectLayer(windows_to_render_front_most[n]); - g.DrawDataBuilder.FlattenIntoSingleLayer(); - - // Draw software mouse cursor if requested - if (g.IO.MouseDrawCursor) - RenderMouseCursor(&g.OverlayDrawList, g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor); - - if (!g.OverlayDrawList.VtxBuffer.empty()) - AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.OverlayDrawList); - - // Setup ImDrawData structure for end-user - SetupDrawData(&g.DrawDataBuilder.Layers[0], &g.DrawData); - g.IO.MetricsRenderVertices = g.DrawData.TotalVtxCount; - g.IO.MetricsRenderIndices = g.DrawData.TotalIdxCount; - - // Render. If user hasn't set a callback then they may retrieve the draw data via GetDrawData() -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - if (g.DrawData.CmdListsCount > 0 && g.IO.RenderDrawListsFn != NULL) - g.IO.RenderDrawListsFn(&g.DrawData); -#endif -} - -const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end) -{ - const char* text_display_end = text; - if (!text_end) - text_end = (const char*)-1; - - while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#')) - text_display_end++; - return text_display_end; -} - -// Internal ImGui functions to render text -// RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText() -void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - // Hide anything after a '##' string - const char* text_display_end; - if (hide_text_after_hash) - { - text_display_end = FindRenderedTextEnd(text, text_end); - } - else - { - if (!text_end) - text_end = text + strlen(text); // FIXME-OPT - text_display_end = text_end; - } - - if (text != text_display_end) - { - window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end); - if (g.LogEnabled) - LogRenderedText(&pos, text, text_display_end); - } -} - -void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - if (!text_end) - text_end = text + strlen(text); // FIXME-OPT - - if (text != text_end) - { - window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width); - if (g.LogEnabled) - LogRenderedText(&pos, text, text_end); - } -} - -// Default clip_rect uses (pos_min,pos_max) -// Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges) -void ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect) -{ - // Hide anything after a '##' string - const char* text_display_end = FindRenderedTextEnd(text, text_end); - const int text_len = (int)(text_display_end - text); - if (text_len == 0) - return; - - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - // Perform CPU side clipping for single clipped element to avoid using scissor state - ImVec2 pos = pos_min; - const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f); - - const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min; - const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max; - bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y); - if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min - need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y); - - // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment. - if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x); - if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y); - - // Render - if (need_clipping) - { - ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y); - window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect); - } - else - { - window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL); - } - if (g.LogEnabled) - LogRenderedText(&pos, text, text_display_end); -} - -// Render a rectangle shaped with optional rounding and borders -void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding); - const float border_size = g.Style.FrameBorderSize; - if (border && border_size > 0.0f) - { - window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size); - window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size); - } -} - -void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - const float border_size = g.Style.FrameBorderSize; - if (border_size > 0.0f) - { - window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size); - window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size); - } -} - -// Render an arrow aimed to be aligned with text (p_min is a position in the same space text would be positioned). To e.g. denote expanded/collapsed state -void ImGui::RenderArrow(ImVec2 p_min, ImGuiDir dir, float scale) -{ - ImGuiContext& g = *GImGui; - - const float h = g.FontSize * 1.00f; - float r = h * 0.40f * scale; - ImVec2 center = p_min + ImVec2(h * 0.50f, h * 0.50f * scale); - - ImVec2 a, b, c; - switch (dir) - { - case ImGuiDir_Up: - case ImGuiDir_Down: - if (dir == ImGuiDir_Up) r = -r; - a = ImVec2(+0.000f,+0.750f) * r; - b = ImVec2(-0.866f,-0.750f) * r; - c = ImVec2(+0.866f,-0.750f) * r; - break; - case ImGuiDir_Left: - case ImGuiDir_Right: - if (dir == ImGuiDir_Left) r = -r; - a = ImVec2(+0.750f,+0.000f) * r; - b = ImVec2(-0.750f,+0.866f) * r; - c = ImVec2(-0.750f,-0.866f) * r; - break; - case ImGuiDir_None: - case ImGuiDir_COUNT: - IM_ASSERT(0); - break; - } - - g.CurrentWindow->DrawList->AddTriangleFilled(center + a, center + b, center + c, GetColorU32(ImGuiCol_Text)); -} - -void ImGui::RenderBullet(ImVec2 pos) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - window->DrawList->AddCircleFilled(pos, GImGui->FontSize*0.20f, GetColorU32(ImGuiCol_Text), 8); -} - -void ImGui::RenderCheckMark(ImVec2 pos, ImU32 col, float sz) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; + AddWindowToDrawDataSelectLayer(windows_to_render_front_most[n]); + g.DrawDataBuilder.FlattenIntoSingleLayer(); - float thickness = ImMax(sz / 5.0f, 1.0f); - sz -= thickness*0.5f; - pos += ImVec2(thickness*0.25f, thickness*0.25f); + // Draw software mouse cursor if requested + if (g.IO.MouseDrawCursor) + RenderMouseCursor(&g.OverlayDrawList, g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor); - float third = sz / 3.0f; - float bx = pos.x + third; - float by = pos.y + sz - third*0.5f; - window->DrawList->PathLineTo(ImVec2(bx - third, by - third)); - window->DrawList->PathLineTo(ImVec2(bx, by)); - window->DrawList->PathLineTo(ImVec2(bx + third*2, by - third*2)); - window->DrawList->PathStroke(col, false, thickness); -} + if (!g.OverlayDrawList.VtxBuffer.empty()) + AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.OverlayDrawList); -void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags) -{ - ImGuiContext& g = *GImGui; - if (id != g.NavId) - return; - if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw)) - return; - ImGuiWindow* window = ImGui::GetCurrentWindow(); - if (window->DC.NavHideHighlightOneFrame) - return; + // Setup ImDrawData structure for end-user + SetupDrawData(&g.DrawDataBuilder.Layers[0], &g.DrawData); + g.IO.MetricsRenderVertices = g.DrawData.TotalVtxCount; + g.IO.MetricsRenderIndices = g.DrawData.TotalIdxCount; - float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding; - ImRect display_rect = bb; - display_rect.ClipWith(window->ClipRect); - if (flags & ImGuiNavHighlightFlags_TypeDefault) - { - const float THICKNESS = 2.0f; - const float DISTANCE = 3.0f + THICKNESS * 0.5f; - display_rect.Expand(ImVec2(DISTANCE,DISTANCE)); - bool fully_visible = window->ClipRect.Contains(display_rect); - if (!fully_visible) - window->DrawList->PushClipRect(display_rect.Min, display_rect.Max); - window->DrawList->AddRect(display_rect.Min + ImVec2(THICKNESS*0.5f,THICKNESS*0.5f), display_rect.Max - ImVec2(THICKNESS*0.5f,THICKNESS*0.5f), GetColorU32(ImGuiCol_NavHighlight), rounding, ImDrawCornerFlags_All, THICKNESS); - if (!fully_visible) - window->DrawList->PopClipRect(); - } - if (flags & ImGuiNavHighlightFlags_TypeThin) - { - window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, ~0, 1.0f); - } + // Render. If user hasn't set a callback then they may retrieve the draw data via GetDrawData() +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + if (g.DrawData.CmdListsCount > 0 && g.IO.RenderDrawListsFn != NULL) + g.IO.RenderDrawListsFn(&g.DrawData); +#endif } // Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker. @@ -3662,32 +3750,41 @@ void ImGui::CalcListClipping(int items_count, float items_height, int* out_items } // Find window given position, search front-to-back -// FIXME: Note that we have a lag here because WindowRectClipped is updated in Begin() so windows moved by user via SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is called, aka before the next Begin(). Moving window thankfully isn't affected. +// FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programatically +// with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is +// called, aka before the next Begin(). Moving window isn't affected. static void FindHoveredWindow() { ImGuiContext& g = *GImGui; ImGuiWindow* hovered_window = NULL; - if (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoInputs)) + if (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs)) hovered_window = g.MovingWindow; + ImVec2 padding_regular = g.Style.TouchExtraPadding; + ImVec2 padding_for_resize_from_edges = g.IO.ConfigResizeWindowsFromEdges ? ImMax(g.Style.TouchExtraPadding, ImVec2(RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS, RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS)) : padding_regular; for (int i = g.Windows.Size - 1; i >= 0 && hovered_window == NULL; i--) { ImGuiWindow* window = g.Windows[i]; if (!window->Active || window->Hidden) continue; - if (window->Flags & ImGuiWindowFlags_NoInputs) + if (window->Flags & ImGuiWindowFlags_NoMouseInputs) continue; // Using the clipped AABB, a child window will typically be clipped by its parent (not always) - ImRect bb(window->OuterRectClipped.Min - g.Style.TouchExtraPadding, window->OuterRectClipped.Max + g.Style.TouchExtraPadding); - if (bb.Contains(g.IO.MousePos)) - { - if (hovered_window == NULL) - hovered_window = window; - if (hovered_window) - break; - } + ImRect bb(window->OuterRectClipped); + if ((window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_NoResize)) + bb.Expand(padding_regular); + else + bb.Expand(padding_for_resize_from_edges); + if (!bb.Contains(g.IO.MousePos)) + continue; + + // Those seemingly unnecessary extra tests are because the code here is a little different in viewport/docking branches. + if (hovered_window == NULL) + hovered_window = window; + if (hovered_window) + break; } g.HoveredWindow = hovered_window; @@ -3709,7 +3806,9 @@ bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool c // Expand for touch input const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding); - return rect_for_touch.Contains(g.IO.MousePos); + if (!rect_for_touch.Contains(g.IO.MousePos)) + return false; + return true; } int ImGui::GetKeyIndex(ImGuiKey imgui_key) @@ -4008,10 +4107,10 @@ static bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size size.y = ImMax(content_avail.y + size.y, 4.0f); SetNextWindowSize(size); - // Name + // Build up name. If you need to append to a same child from multiple location in the ID stack, use BeginChild(ImGuiID id) with a stable value. char title[256]; if (name) - ImFormatString(title, IM_ARRAYSIZE(title), "%s/%s", parent_window->Name, name); + ImFormatString(title, IM_ARRAYSIZE(title), "%s/%s_%08X", parent_window->Name, name, id); else ImFormatString(title, IM_ARRAYSIZE(title), "%s/%08X", parent_window->Name, id); @@ -4130,13 +4229,18 @@ static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, b window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags); } -ImGuiWindow* ImGui::FindWindowByName(const char* name) +ImGuiWindow* ImGui::FindWindowByID(ImGuiID id) { ImGuiContext& g = *GImGui; - ImGuiID id = ImHash(name, 0); return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id); } +ImGuiWindow* ImGui::FindWindowByName(const char* name) +{ + ImGuiID id = ImHash(name, 0); + return FindWindowByID(id); +} + static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags) { ImGuiContext& g = *GImGui; @@ -4161,7 +4265,7 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFl if (ImLengthSqr(settings->Size) > 0.00001f) size = ImFloor(settings->Size); } - window->Size = window->SizeFull = window->SizeFullAtLastBegin = size; + window->Size = window->SizeFull = window->SizeFullAtLastBegin = ImFloor(size); window->DC.CursorMaxPos = window->Pos; // So first call to CalcSizeContents() doesn't return crazy values if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0) @@ -4178,8 +4282,9 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFl window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0); } + g.WindowsFocusOrder.push_back(window); if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus) - g.Windows.insert(g.Windows.begin(), window); // Quite slow but rare and only once + g.Windows.push_front(window); // Quite slow but rare and only once else g.Windows.push_back(window); return window; @@ -4256,12 +4361,12 @@ ImVec2 ImGui::CalcWindowExpectedSize(ImGuiWindow* window) return CalcSizeAfterConstraint(window, CalcSizeAutoFit(window, size_contents)); } -static float GetScrollMaxX(ImGuiWindow* window) +float ImGui::GetWindowScrollMaxX(ImGuiWindow* window) { return ImMax(0.0f, window->SizeContents.x - (window->SizeFull.x - window->ScrollbarSizes.x)); } -static float GetScrollMaxY(ImGuiWindow* window) +float ImGui::GetWindowScrollMaxY(ImGuiWindow* window) { return ImMax(0.0f, window->SizeContents.y - (window->SizeFull.y - window->ScrollbarSizes.y)); } @@ -4289,8 +4394,8 @@ static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool s scroll = ImMax(scroll, ImVec2(0.0f, 0.0f)); if (!window->Collapsed && !window->SkipItems) { - scroll.x = ImMin(scroll.x, GetScrollMaxX(window)); - scroll.y = ImMin(scroll.y, GetScrollMaxY(window)); + scroll.x = ImMin(scroll.x, ImGui::GetWindowScrollMaxX(window)); + scroll.y = ImMin(scroll.y, ImGui::GetWindowScrollMaxY(window)); } return scroll; } @@ -4333,14 +4438,14 @@ const ImGuiResizeGripDef resize_grip_def[4] = { ImVec2(1,0), ImVec2(-1,+1), 9,12 }, // Upper right }; -static ImRect GetBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness) +static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness) { ImRect rect = window->Rect(); if (thickness == 0.0f) rect.Max -= ImVec2(1,1); - if (border_n == 0) return ImRect(rect.Min.x + perp_padding, rect.Min.y, rect.Max.x - perp_padding, rect.Min.y + thickness); - if (border_n == 1) return ImRect(rect.Max.x - thickness, rect.Min.y + perp_padding, rect.Max.x, rect.Max.y - perp_padding); - if (border_n == 2) return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness, rect.Max.x - perp_padding, rect.Max.y); - if (border_n == 3) return ImRect(rect.Min.x, rect.Min.y + perp_padding, rect.Min.x + thickness, rect.Max.y - perp_padding); + if (border_n == 0) return ImRect(rect.Min.x + perp_padding, rect.Min.y - thickness, rect.Max.x - perp_padding, rect.Min.y + thickness); + if (border_n == 1) return ImRect(rect.Max.x - thickness, rect.Min.y + perp_padding, rect.Max.x + thickness, rect.Max.y - perp_padding); + if (border_n == 2) return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness, rect.Max.x - perp_padding, rect.Max.y + thickness); + if (border_n == 3) return ImRect(rect.Min.x - thickness, rect.Min.y + perp_padding, rect.Min.x + thickness, rect.Max.y - perp_padding); IM_ASSERT(0); return ImRect(); } @@ -4352,10 +4457,13 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au ImGuiWindowFlags flags = window->Flags; if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0) return; + if (window->WasActive == false) // Early out to avoid running this code for e.g. an hidden implicit Debug window. + return; const int resize_border_count = g.IO.ConfigResizeWindowsFromEdges ? 4 : 0; const float grip_draw_size = (float)(int)ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f); - const float grip_hover_size = (float)(int)(grip_draw_size * 0.75f); + const float grip_hover_inner_size = (float)(int)(grip_draw_size * 0.75f); + const float grip_hover_outer_size = g.IO.ConfigResizeWindowsFromEdges ? RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS : 0.0f; ImVec2 pos_target(FLT_MAX, FLT_MAX); ImVec2 size_target(FLT_MAX, FLT_MAX); @@ -4368,11 +4476,12 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPos); // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window - ImRect resize_rect(corner, corner + grip.InnerDir * grip_hover_size); + ImRect resize_rect(corner - grip.InnerDir * grip_hover_outer_size, corner + grip.InnerDir * grip_hover_inner_size); if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x); if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y); bool hovered, held; ButtonBehavior(resize_rect, window->GetID((void*)(intptr_t)resize_grip_n), &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus); + //GetOverlayDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255)); if (hovered || held) g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE; @@ -4386,7 +4495,7 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au { // Resize from any of the four corners // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position - ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + resize_rect.GetSize() * grip.CornerPos; // Corner of the window corresponding to our corner grip + ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + ImLerp(grip.InnerDir * grip_hover_outer_size, grip.InnerDir * -grip_hover_inner_size, grip.CornerPos); // Corner of the window corresponding to our corner grip CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerPos, &pos_target, &size_target); } if (resize_grip_n == 0 || held || hovered) @@ -4394,12 +4503,11 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au } for (int border_n = 0; border_n < resize_border_count; border_n++) { - const float BORDER_SIZE = 5.0f; // FIXME: Only works _inside_ window because of HoveredWindow check. - const float BORDER_APPEAR_TIMER = 0.05f; // Reduce visual noise bool hovered, held; - ImRect border_rect = GetBorderRect(window, border_n, grip_hover_size, BORDER_SIZE); + ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS); ButtonBehavior(border_rect, window->GetID((void*)(intptr_t)(border_n + 4)), &hovered, &held, ImGuiButtonFlags_FlattenChildren); - if ((hovered && g.HoveredIdTimer > BORDER_APPEAR_TIMER) || held) + //GetOverlayDrawList(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255)); + if ((hovered && g.HoveredIdTimer > RESIZE_WINDOWS_FROM_EDGES_FEEDBACK_TIMER) || held) { g.MouseCursor = (border_n & 1) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS; if (held) *border_held = border_n; @@ -4408,10 +4516,10 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au { ImVec2 border_target = window->Pos; ImVec2 border_posn; - if (border_n == 0) { border_posn = ImVec2(0, 0); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y); } - if (border_n == 1) { border_posn = ImVec2(1, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + BORDER_SIZE); } - if (border_n == 2) { border_posn = ImVec2(0, 1); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + BORDER_SIZE); } - if (border_n == 3) { border_posn = ImVec2(0, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x); } + if (border_n == 0) { border_posn = ImVec2(0, 0); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS); } + if (border_n == 1) { border_posn = ImVec2(1, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS); } + if (border_n == 2) { border_posn = ImVec2(0, 1); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS); } + if (border_n == 3) { border_posn = ImVec2(0, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS); } CalcResizePosSizeFromAnyCorner(window, border_target, border_posn, &pos_target, &size_target); } } @@ -4489,7 +4597,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } // Automatically disable manual moving/resizing when NoInputs is set - if (flags & ImGuiWindowFlags_NoInputs) + if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs) flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize; if (flags & ImGuiWindowFlags_NavFlattened) @@ -4819,13 +4927,14 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) else { // Window background - ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags)); - if (g.NextWindowData.BgAlphaCond != 0) + if (!(flags & ImGuiWindowFlags_NoBackground)) { - bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(g.NextWindowData.BgAlphaVal) << IM_COL32_A_SHIFT); - g.NextWindowData.BgAlphaCond = 0; + ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags)); + if (g.NextWindowData.BgAlphaCond != 0) + bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(g.NextWindowData.BgAlphaVal) << IM_COL32_A_SHIFT); + window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot); } - window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot); + g.NextWindowData.BgAlphaCond = 0; // Title bar ImU32 title_bar_col = GetColorU32(window->Collapsed ? ImGuiCol_TitleBgCollapsed : title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg); @@ -4863,11 +4972,11 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } // Borders - if (window_border_size > 0.0f) + if (window_border_size > 0.0f && !(flags & ImGuiWindowFlags_NoBackground)) window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), window_rounding, ImDrawCornerFlags_All, window_border_size); if (border_held != -1) { - ImRect border = GetBorderRect(window, border_held, grip_draw_size, 0.0f); + ImRect border = GetResizeBorderRect(window, border_held, grip_draw_size, 0.0f); window->DrawList->AddLine(border.Min, border.Max, GetColorU32(ImGuiCol_SeparatorActive), ImMax(1.0f, window_border_size)); } if (style.FrameBorderSize > 0 && !(flags & ImGuiWindowFlags_NoTitleBar)) @@ -5000,7 +5109,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) /* if (g.ActiveId == move_id) if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C)) - ImGui::LogToClipboard(); + LogToClipboard(); */ // Inner rectangle @@ -5068,13 +5177,13 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_first_use, { // Old API feature: we could pass the initial window size as a parameter. This was misleading because it only had an effect if the window didn't have data in the .ini file. if (size_first_use.x != 0.0f || size_first_use.y != 0.0f) - ImGui::SetNextWindowSize(size_first_use, ImGuiCond_FirstUseEver); + SetNextWindowSize(size_first_use, ImGuiCond_FirstUseEver); // Old API feature: override the window background alpha with a parameter. if (bg_alpha_override >= 0.0f) - ImGui::SetNextWindowBgAlpha(bg_alpha_override); + SetNextWindowBgAlpha(bg_alpha_override); - return ImGui::Begin(name, p_open, flags); + return Begin(name, p_open, flags); } #endif // IMGUI_DISABLE_OBSOLETE_FUNCTIONS @@ -5099,7 +5208,21 @@ void ImGui::End() SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back()); } -void ImGui::BringWindowToFront(ImGuiWindow* window) +void ImGui::BringWindowToFocusFront(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + if (g.WindowsFocusOrder.back() == window) + return; + for (int i = g.WindowsFocusOrder.Size - 2; i >= 0; i--) // We can ignore the front most window + if (g.WindowsFocusOrder[i] == window) + { + memmove(&g.WindowsFocusOrder[i], &g.WindowsFocusOrder[i + 1], (size_t)(g.WindowsFocusOrder.Size - i - 1) * sizeof(ImGuiWindow*)); + g.WindowsFocusOrder[g.WindowsFocusOrder.Size - 1] = window; + break; + } +} + +void ImGui::BringWindowToDisplayFront(ImGuiWindow* window) { ImGuiContext& g = *GImGui; ImGuiWindow* current_front_window = g.Windows.back(); @@ -5108,13 +5231,13 @@ void ImGui::BringWindowToFront(ImGuiWindow* window) for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the front most window if (g.Windows[i] == window) { - g.Windows.erase(g.Windows.Data + i); - g.Windows.push_back(window); + memmove(&g.Windows[i], &g.Windows[i + 1], (size_t)(g.Windows.Size - i - 1) * sizeof(ImGuiWindow*)); + g.Windows[g.Windows.Size - 1] = window; break; } } -void ImGui::BringWindowToBack(ImGuiWindow* window) +void ImGui::BringWindowToDisplayBack(ImGuiWindow* window) { ImGuiContext& g = *GImGui; if (g.Windows[0] == window) @@ -5159,20 +5282,24 @@ void ImGui::FocusWindow(ImGuiWindow* window) ClearActiveID(); // Bring to front + BringWindowToFocusFront(window); if (!(window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) - BringWindowToFront(window); + BringWindowToDisplayFront(window); } -void ImGui::FocusFrontMostActiveWindowIgnoringOne(ImGuiWindow* ignore_window) +void ImGui::FocusPreviousWindowIgnoringOne(ImGuiWindow* ignore_window) { ImGuiContext& g = *GImGui; - for (int i = g.Windows.Size - 1; i >= 0; i--) - if (g.Windows[i] != ignore_window && g.Windows[i]->WasActive && !(g.Windows[i]->Flags & ImGuiWindowFlags_ChildWindow)) + for (int i = g.WindowsFocusOrder.Size - 1; i >= 0; i--) + { + ImGuiWindow* window = g.WindowsFocusOrder[i]; + if (window != ignore_window && window->WasActive && !(window->Flags & ImGuiWindowFlags_ChildWindow)) { - ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(g.Windows[i]); + ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(window); FocusWindow(focus_window); return; } + } } void ImGui::PushItemWidth(float item_width) @@ -5267,9 +5394,10 @@ void ImGui::PopItemFlag() window->DC.ItemFlags = window->DC.ItemFlagsStack.empty() ? ImGuiItemFlags_Default_ : window->DC.ItemFlagsStack.back(); } +// FIXME: Look into renaming this once we have settled the new Focus/Activation/TabStop system. void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus) { - PushItemFlag(ImGuiItemFlags_AllowKeyboardFocus, allow_keyboard_focus); + PushItemFlag(ImGuiItemFlags_NoTabStop, !allow_keyboard_focus); } void ImGui::PopAllowKeyboardFocus() @@ -5528,11 +5656,11 @@ bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags) { ImGuiContext& g = *GImGui; - IM_ASSERT(g.CurrentWindow); // Not inside a Begin()/End() if (flags & ImGuiFocusedFlags_AnyWindow) return g.NavWindow != NULL; + IM_ASSERT(g.CurrentWindow); // Not inside a Begin()/End() switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows)) { case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows: @@ -5633,7 +5761,7 @@ static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond con if (size.x > 0.0f) { window->AutoFitFramesX = 0; - window->SizeFull.x = size.x; + window->SizeFull.x = ImFloor(size.x); } else { @@ -5643,7 +5771,7 @@ static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond con if (size.y > 0.0f) { window->AutoFitFramesY = 0; - window->SizeFull.y = size.y; + window->SizeFull.y = ImFloor(size.y); } else { @@ -5934,12 +6062,12 @@ float ImGui::GetScrollY() float ImGui::GetScrollMaxX() { - return GetScrollMaxX(GImGui->CurrentWindow); + return GetWindowScrollMaxX(GImGui->CurrentWindow); } float ImGui::GetScrollMaxY() { - return GetScrollMaxY(GImGui->CurrentWindow); + return GetWindowScrollMaxY(GImGui->CurrentWindow); } void ImGui::SetScrollX(float scroll_x) @@ -5966,7 +6094,7 @@ void ImGui::SetScrollFromPosY(float pos_y, float center_y_ratio) } // center_y_ratio: 0.0f top of last item, 0.5f vertical center of last item, 1.0f bottom of last item. -void ImGui::SetScrollHere(float center_y_ratio) +void ImGui::SetScrollHereY(float center_y_ratio) { ImGuiWindow* window = GetCurrentWindow(); float target_y = window->DC.CursorPosPrevLine.y - window->Pos.y; // Top of last item, in window space @@ -6001,7 +6129,7 @@ void ImGui::SetItemDefaultFocus() g.NavInitResultRectRel = ImRect(g.NavWindow->DC.LastItemRect.Min - g.NavWindow->Pos, g.NavWindow->DC.LastItemRect.Max - g.NavWindow->Pos); NavUpdateAnyRequestFlag(); if (!IsItemVisible()) - SetScrollHere(); + SetScrollHereY(); } } @@ -6017,17 +6145,6 @@ ImGuiStorage* ImGui::GetStateStorage() return window->DC.StateStorage; } -void ImGui::AlignTextToFramePadding() -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - ImGuiContext& g = *GImGui; - window->DC.CurrentLineSize.y = ImMax(window->DC.CurrentLineSize.y, g.FontSize + g.Style.FramePadding.y * 2); - window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.CurrentLineTextBaseOffset, g.Style.FramePadding.y); -} - void ImGui::PushID(const char* str_id) { ImGuiWindow* window = GetCurrentWindowRead(); @@ -6074,149 +6191,6 @@ ImGuiID ImGui::GetID(const void* ptr_id) return GImGui->CurrentWindow->GetID(ptr_id); } -// Horizontal/vertical separating line -void ImGui::Separator() -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - ImGuiContext& g = *GImGui; - - // Those flags should eventually be overridable by the user - ImGuiSeparatorFlags flags = (window->DC.LayoutType == ImGuiLayoutType_Horizontal) ? ImGuiSeparatorFlags_Vertical : ImGuiSeparatorFlags_Horizontal; - IM_ASSERT(ImIsPowerOfTwo((int)(flags & (ImGuiSeparatorFlags_Horizontal | ImGuiSeparatorFlags_Vertical)))); // Check that only 1 option is selected - if (flags & ImGuiSeparatorFlags_Vertical) - { - VerticalSeparator(); - return; - } - - // Horizontal Separator - if (window->DC.ColumnsSet) - PopClipRect(); - - float x1 = window->Pos.x; - float x2 = window->Pos.x + window->Size.x; - if (!window->DC.GroupStack.empty()) - x1 += window->DC.Indent.x; - - const ImRect bb(ImVec2(x1, window->DC.CursorPos.y), ImVec2(x2, window->DC.CursorPos.y+1.0f)); - ItemSize(ImVec2(0.0f, 0.0f)); // NB: we don't provide our width so that it doesn't get feed back into AutoFit, we don't provide height to not alter layout. - if (!ItemAdd(bb, 0)) - { - if (window->DC.ColumnsSet) - PushColumnClipRect(); - return; - } - - window->DrawList->AddLine(bb.Min, ImVec2(bb.Max.x,bb.Min.y), GetColorU32(ImGuiCol_Separator)); - - if (g.LogEnabled) - LogRenderedText(NULL, IM_NEWLINE "--------------------------------"); - - if (window->DC.ColumnsSet) - { - PushColumnClipRect(); - window->DC.ColumnsSet->LineMinY = window->DC.CursorPos.y; - } -} - -void ImGui::VerticalSeparator() -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - ImGuiContext& g = *GImGui; - - float y1 = window->DC.CursorPos.y; - float y2 = window->DC.CursorPos.y + window->DC.CurrentLineSize.y; - const ImRect bb(ImVec2(window->DC.CursorPos.x, y1), ImVec2(window->DC.CursorPos.x + 1.0f, y2)); - ItemSize(ImVec2(bb.GetWidth(), 0.0f)); - if (!ItemAdd(bb, 0)) - return; - - window->DrawList->AddLine(ImVec2(bb.Min.x, bb.Min.y), ImVec2(bb.Min.x, bb.Max.y), GetColorU32(ImGuiCol_Separator)); - if (g.LogEnabled) - LogText(" |"); -} - -// Using 'hover_visibility_delay' allows us to hide the highlight and mouse cursor for a short time, which can be convenient to reduce visual noise. -bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend, float hover_visibility_delay) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags; - window->DC.ItemFlags |= ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus; - bool item_add = ItemAdd(bb, id); - window->DC.ItemFlags = item_flags_backup; - if (!item_add) - return false; - - bool hovered, held; - ImRect bb_interact = bb; - bb_interact.Expand(axis == ImGuiAxis_Y ? ImVec2(0.0f, hover_extend) : ImVec2(hover_extend, 0.0f)); - ButtonBehavior(bb_interact, id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap); - if (g.ActiveId != id) - SetItemAllowOverlap(); - - if (held || (g.HoveredId == id && g.HoveredIdPreviousFrame == id && g.HoveredIdTimer >= hover_visibility_delay)) - SetMouseCursor(axis == ImGuiAxis_Y ? ImGuiMouseCursor_ResizeNS : ImGuiMouseCursor_ResizeEW); - - ImRect bb_render = bb; - if (held) - { - ImVec2 mouse_delta_2d = g.IO.MousePos - g.ActiveIdClickOffset - bb_interact.Min; - float mouse_delta = (axis == ImGuiAxis_Y) ? mouse_delta_2d.y : mouse_delta_2d.x; - - // Minimum pane size - float size_1_maximum_delta = ImMax(0.0f, *size1 - min_size1); - float size_2_maximum_delta = ImMax(0.0f, *size2 - min_size2); - if (mouse_delta < -size_1_maximum_delta) - mouse_delta = -size_1_maximum_delta; - if (mouse_delta > size_2_maximum_delta) - mouse_delta = size_2_maximum_delta; - - // Apply resize - if (mouse_delta != 0.0f) - { - if (mouse_delta < 0.0f) - IM_ASSERT(*size1 + mouse_delta >= min_size1); - if (mouse_delta > 0.0f) - IM_ASSERT(*size2 - mouse_delta >= min_size2); - *size1 += mouse_delta; - *size2 -= mouse_delta; - bb_render.Translate((axis == ImGuiAxis_X) ? ImVec2(mouse_delta, 0.0f) : ImVec2(0.0f, mouse_delta)); - MarkItemEdited(id); - } - } - - // Render - const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : (hovered && g.HoveredIdTimer >= hover_visibility_delay) ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator); - window->DrawList->AddRectFilled(bb_render.Min, bb_render.Max, col, g.Style.FrameRounding); - - return held; -} - -void ImGui::Spacing() -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - ItemSize(ImVec2(0,0)); -} - -void ImGui::Dummy(const ImVec2& size) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); - ItemSize(bb); - ItemAdd(bb, 0); -} - bool ImGui::IsRectVisible(const ImVec2& size) { ImGuiWindow* window = GetCurrentWindowRead(); @@ -6323,22 +6297,6 @@ void ImGui::SameLine(float pos_x, float spacing_w) window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset; } -void ImGui::NewLine() -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - ImGuiContext& g = *GImGui; - const ImGuiLayoutType backup_layout_type = window->DC.LayoutType; - window->DC.LayoutType = ImGuiLayoutType_Vertical; - if (window->DC.CurrentLineSize.y > 0.0f) // In the event that we are on a line with items that is smaller that FontSize high, we will preserve its height. - ItemSize(ImVec2(0,0)); - else - ItemSize(ImVec2(0.0f, g.FontSize)); - window->DC.LayoutType = backup_layout_type; -} - void ImGui::Indent(float indent_w) { ImGuiContext& g = *GImGui; @@ -6356,7 +6314,7 @@ void ImGui::Unindent(float indent_w) } //----------------------------------------------------------------------------- -// TOOLTIPS +// [SECTION] TOOLTIPS //----------------------------------------------------------------------------- void ImGui::BeginTooltip() @@ -6395,7 +6353,7 @@ void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_ window->HiddenFramesRegular = 1; ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount); } - ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoInputs|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoNav; + ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoInputs|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize; Begin(window_name, NULL, flags | extra_flags); } @@ -6425,7 +6383,7 @@ void ImGui::SetTooltip(const char* fmt, ...) } //----------------------------------------------------------------------------- -// POPUPS +// [SECTION] POPUPS //----------------------------------------------------------------------------- bool ImGui::IsPopupOpen(ImGuiID id) @@ -6471,8 +6429,8 @@ void ImGui::OpenPopupEx(ImGuiID id) popup_ref.ParentWindow = parent_window; popup_ref.OpenFrameCount = g.FrameCount; popup_ref.OpenParentId = parent_window->IDStack.back(); - popup_ref.OpenMousePos = g.IO.MousePos; popup_ref.OpenPopupPos = NavCalcPreferredRefPos(); + popup_ref.OpenMousePos = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos; //printf("[%05d] OpenPopupEx(0x%08X)\n", g.FrameCount, id); if (g.OpenPopupStack.Size < current_stack_size + 1) @@ -6792,10 +6750,10 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) } //----------------------------------------------------------------------------- -// NAVIGATION +// [SECTION] KEYBOARD/GAMEPAD NAVIGATION //----------------------------------------------------------------------------- -static ImGuiDir inline NavScoreItemGetQuadrant(float dx, float dy) +ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy) { if (ImFabs(dx) > ImFabs(dy)) return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left; @@ -6871,7 +6829,7 @@ static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) dax = dbx; day = dby; dist_axial = dist_box; - quadrant = NavScoreItemGetQuadrant(dbx, dby); + quadrant = ImGetDirQuadrantFromDelta(dbx, dby); } else if (dcx != 0.0f || dcy != 0.0f) { @@ -6879,7 +6837,7 @@ static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) dax = dcx; day = dcy; dist_axial = dist_center; - quadrant = NavScoreItemGetQuadrant(dcx, dcy); + quadrant = ImGetDirQuadrantFromDelta(dcx, dcy); } else { @@ -6892,7 +6850,7 @@ static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) if (ImGui::IsMouseHoveringRect(cand.Min, cand.Max)) { ImFormatString(buf, IM_ARRAYSIZE(buf), "dbox (%.2f,%.2f->%.4f)\ndcen (%.2f,%.2f->%.4f)\nd (%.2f,%.2f->%.4f)\nnav %c, quadrant %c", dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, "WENS"[g.NavMoveDir], "WENS"[quadrant]); - ImDrawList* draw_list = ImGui::GetOverlayDrawList(); + ImDrawList* draw_list = GetOverlayDrawList(window); draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100)); draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200)); draw_list->AddRectFilled(cand.Max-ImVec2(4,4), cand.Max+ImGui::CalcTextSize(buf)+ImVec2(4,4), IM_COL32(40,0,0,150)); @@ -6900,11 +6858,11 @@ static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) } else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate. { - if (IsKeyPressedMap(ImGuiKey_C)) { g.NavMoveDirLast = (ImGuiDir)((g.NavMoveDirLast + 1) & 3); g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_C]] = 0.01f; } + if (ImGui::IsKeyPressedMap(ImGuiKey_C)) { g.NavMoveDirLast = (ImGuiDir)((g.NavMoveDirLast + 1) & 3); g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_C]] = 0.01f; } if (quadrant == g.NavMoveDir) { ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center); - ImDrawList* draw_list = ImGui::GetOverlayDrawList(); + ImDrawList* draw_list = GetOverlayDrawList(window); draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200)); draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf); } @@ -7149,13 +7107,20 @@ static ImVec2 ImGui::NavCalcPreferredRefPos() { ImGuiContext& g = *GImGui; if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow) - return ImFloor(g.IO.MousePos); - - // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item - const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer]; - ImVec2 pos = g.NavWindow->Pos + ImVec2(rect_rel.Min.x + ImMin(g.Style.FramePadding.x*4, rect_rel.GetWidth()), rect_rel.Max.y - ImMin(g.Style.FramePadding.y, rect_rel.GetHeight())); - ImRect visible_rect = GetViewportRect(); - return ImFloor(ImClamp(pos, visible_rect.Min, visible_rect.Max)); // ImFloor() is important because non-integer mouse position application in back-end might be lossy and result in undesirable non-zero delta. + { + // Mouse (we need a fallback in case the mouse becomes invalid after being used) + if (IsMousePosValid(&g.IO.MousePos)) + return g.IO.MousePos; + return g.LastValidMousePos; + } + else + { + // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item. + const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer]; + ImVec2 pos = g.NavWindow->Pos + ImVec2(rect_rel.Min.x + ImMin(g.Style.FramePadding.x * 4, rect_rel.GetWidth()), rect_rel.Max.y - ImMin(g.Style.FramePadding.y, rect_rel.GetHeight())); + ImRect visible_rect = GetViewportRect(); + return ImFloor(ImClamp(pos, visible_rect.Min, visible_rect.Max)); // ImFloor() is important because non-integer mouse position application in back-end might be lossy and result in undesirable non-zero delta. + } } float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode) @@ -7280,7 +7245,7 @@ static void ImGui::NavUpdate() g.NavJustMovedToId = 0; // Process navigation move request - if (g.NavMoveRequest && (g.NavMoveResultLocal.ID != 0 || g.NavMoveResultOther.ID != 0)) + if (g.NavMoveRequest) NavUpdateMoveResult(); // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame @@ -7490,15 +7455,27 @@ static void ImGui::NavUpdate() //g.OverlayDrawList.AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG] g.NavScoringCount = 0; #if IMGUI_DEBUG_NAV_RECTS - if (g.NavWindow) { for (int layer = 0; layer < 2; layer++) GetOverlayDrawList()->AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG] - if (g.NavWindow) { ImU32 col = (g.NavWindow->HiddenFrames == 0) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredRefPos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); g.OverlayDrawList.AddCircleFilled(p, 3.0f, col); g.OverlayDrawList.AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); } + if (g.NavWindow) { for (int layer = 0; layer < 2; layer++) GetOverlayDrawList(g.NavWindow)->AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG] + if (g.NavWindow) { ImU32 col = (!g.NavWindow->Hidden) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredRefPos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); GetOverlayDrawList(g.NavWindow)->AddCircleFilled(p, 3.0f, col); GetOverlayDrawList(g.NavWindow)->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); } #endif } +// Apply result from previous frame navigation directional move request static void ImGui::NavUpdateMoveResult() { - // Select which result to use ImGuiContext& g = *GImGui; + if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0) + { + // In a situation when there is no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result) + if (g.NavId != 0) + { + g.NavDisableHighlight = false; + g.NavDisableMouseHover = true; + } + return; + } + + // Select which result to use ImGuiNavMoveResult* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page. @@ -7528,7 +7505,6 @@ static void ImGui::NavUpdateMoveResult() NavScrollToBringItemIntoView(result->Window->ParentWindow, ImRect(rect_abs.Min + delta_scroll, rect_abs.Max + delta_scroll)); } - // Apply result from previous frame navigation directional move request ClearActiveID(); g.NavWindow = result->Window; SetNavIDWithRectRel(result->ID, g.NavLayer, result->RectRel); @@ -7580,11 +7556,11 @@ static float ImGui::NavUpdatePageUpPageDown(int allowed_dir_flags) return 0.0f; } -static int FindWindowIndex(ImGuiWindow* window) // FIXME-OPT O(N) +static int FindWindowFocusIndex(ImGuiWindow* window) // FIXME-OPT O(N) { ImGuiContext& g = *GImGui; - for (int i = g.Windows.Size-1; i >= 0; i--) - if (g.Windows[i] == window) + for (int i = g.WindowsFocusOrder.Size-1; i >= 0; i--) + if (g.WindowsFocusOrder[i] == window) return i; return -1; } @@ -7592,9 +7568,9 @@ static int FindWindowIndex(ImGuiWindow* window) // FIXME-OPT O(N) static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N) { ImGuiContext& g = *GImGui; - for (int i = i_start; i >= 0 && i < g.Windows.Size && i != i_stop; i += dir) - if (ImGui::IsWindowNavFocusable(g.Windows[i])) - return g.Windows[i]; + for (int i = i_start; i >= 0 && i < g.WindowsFocusOrder.Size && i != i_stop; i += dir) + if (ImGui::IsWindowNavFocusable(g.WindowsFocusOrder[i])) + return g.WindowsFocusOrder[i]; return NULL; } @@ -7605,10 +7581,10 @@ static void NavUpdateWindowingHighlightWindow(int focus_change_dir) if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal) return; - const int i_current = FindWindowIndex(g.NavWindowingTarget); + const int i_current = FindWindowFocusIndex(g.NavWindowingTarget); ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir); if (!window_target) - window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.Windows.Size - 1) : 0, i_current, focus_change_dir); + window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir); if (window_target) // Don't reset windowing target if there's a single window in the list g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target; g.NavWindowingToggleLayer = false; @@ -7640,7 +7616,7 @@ static void ImGui::NavUpdateWindowing() bool start_windowing_with_gamepad = !g.NavWindowingTarget && IsNavInputPressed(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed); bool start_windowing_with_keyboard = !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard); if (start_windowing_with_gamepad || start_windowing_with_keyboard) - if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.Windows.Size - 1, -INT_MAX, -1)) + if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1)) { g.NavWindowingTarget = g.NavWindowingTargetAnim = window; g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f; @@ -7771,10 +7747,10 @@ void ImGui::NavUpdateWindowingList() SetNextWindowSizeConstraints(ImVec2(g.IO.DisplaySize.x * 0.20f, g.IO.DisplaySize.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX)); SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f); - Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings); - for (int n = g.Windows.Size - 1; n >= 0; n--) + Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings); + for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--) { - ImGuiWindow* window = g.Windows[n]; + ImGuiWindow* window = g.WindowsFocusOrder[n]; if (!IsWindowNavFocusable(window)) continue; const char* label = window->Name; @@ -7787,7 +7763,8 @@ void ImGui::NavUpdateWindowingList() } //----------------------------------------------------------------------------- -// COLUMNS +// [SECTION] COLUMNS +// In the current version, Columns are very weak. Needs to be replaced with a more full-featured system. //----------------------------------------------------------------------------- void ImGui::NextColumn() @@ -8109,7 +8086,7 @@ void ImGui::Columns(int columns_count, const char* id, bool border) } //----------------------------------------------------------------------------- -// DRAG AND DROP +// [SECTION] DRAG AND DROP //----------------------------------------------------------------------------- void ImGui::ClearDragDrop() @@ -8386,6 +8363,12 @@ const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDrop return &payload; } +const ImGuiPayload* ImGui::GetDragDropPayload() +{ + ImGuiContext& g = *GImGui; + return g.DragDropActive ? &g.DragDropPayload : NULL; +} + // We don't really use/need this now, but added it for the sake of consistency and because we might need it later. void ImGui::EndDragDropTarget() { @@ -8396,7 +8379,7 @@ void ImGui::EndDragDropTarget() } //----------------------------------------------------------------------------- -// LOGGING +// [SECTION] LOGGING/CAPTURING //----------------------------------------------------------------------------- // Pass text data straight to log (without being displayed) @@ -8436,29 +8419,17 @@ void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* for (;;) { // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry. - const char* line_end = text_remaining; - while (line_end < text_end) - if (*line_end == '\n') - break; - else - line_end++; - if (line_end >= text_end) - line_end = NULL; - - const bool is_first_line = (text == text_remaining); - bool is_last_line = false; - if (line_end == NULL) + const char* line_start = text_remaining; + const char* line_end = ImStreolRange(line_start, text_end); + const bool is_first_line = (line_start == text); + const bool is_last_line = (line_end == text_end); + if (!is_last_line || (line_start != line_end)) { - is_last_line = true; - line_end = text_end; - } - if (line_end != NULL && !(is_last_line && (line_end - text_remaining)==0)) - { - const int char_count = (int)(line_end - text_remaining); + const int char_count = (int)(line_end - line_start); if (log_new_line || !is_first_line) - LogText(IM_NEWLINE "%*s%.*s", tree_depth*4, "", char_count, text_remaining); + LogText(IM_NEWLINE "%*s%.*s", tree_depth*4, "", char_count, line_start); else - LogText(" %.*s", char_count, text_remaining); + LogText(" %.*s", char_count, line_start); } if (is_last_line) @@ -8576,7 +8547,7 @@ void ImGui::LogButtons() } //----------------------------------------------------------------------------- -// SETTINGS +// [SECTION] SETTINGS //----------------------------------------------------------------------------- void ImGui::MarkIniSettingsDirty() @@ -8594,7 +8565,7 @@ void ImGui::MarkIniSettingsDirty(ImGuiWindow* window) g.SettingsDirtyTimer = g.IO.IniSavingRate; } -static ImGuiWindowSettings* CreateNewWindowSettings(const char* name) +ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name) { ImGuiContext& g = *GImGui; g.SettingsWindows.push_back(ImGuiWindowSettings()); @@ -8662,7 +8633,8 @@ void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size) while (line_end < buf_end && *line_end != '\n' && *line_end != '\r') line_end++; line_end[0] = 0; - + if (line[0] == ';') + continue; if (line[0] == '[' && line_end > line && line_end[-1] == ']') { // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code. @@ -8731,7 +8703,7 @@ static void* SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler* { ImGuiWindowSettings* settings = ImGui::FindWindowSettings(ImHash(name, 0)); if (!settings) - settings = CreateNewWindowSettings(name); + settings = ImGui::CreateNewWindowSettings(name); return (void*)settings; } @@ -8748,6 +8720,7 @@ static void SettingsHandlerWindow_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) { // Gather data from windows that were active during this session + // (if a window wasn't opened in this session we preserve its settings) ImGuiContext& g = *imgui_ctx; for (int i = 0; i != g.Windows.Size; i++) { @@ -8758,7 +8731,7 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSetting ImGuiWindowSettings* settings = (window->SettingsIdx != -1) ? &g.SettingsWindows[window->SettingsIdx] : ImGui::FindWindowSettings(window->ID); if (!settings) { - settings = CreateNewWindowSettings(window->Name); + settings = ImGui::CreateNewWindowSettings(window->Name); window->SettingsIdx = g.SettingsWindows.index_from_pointer(settings); } IM_ASSERT(settings->ID == window->ID); @@ -8767,8 +8740,7 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSetting settings->Collapsed = window->Collapsed; } - // Write a buffer - // If a window wasn't opened in this session we preserve its settings + // Write to text buffer buf->reserve(buf->size() + g.SettingsWindows.Size * 96); // ballpark reserve for (int i = 0; i != g.SettingsWindows.Size; i++) { @@ -8787,10 +8759,10 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSetting } //----------------------------------------------------------------------------- -// PLATFORM DEPENDENT HELPERS +// [SECTION] PLATFORM DEPENDENT HELPERS //----------------------------------------------------------------------------- -#if defined(_WIN32) && !defined(_WINDOWS_) && (!defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) || !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)) +#if defined(_WIN32) && !defined(_WINDOWS_) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && (!defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) || !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)) #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif @@ -8802,7 +8774,7 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSetting #endif // Win32 API clipboard implementation -#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) +#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) #ifdef _MSC_VER #pragma comment(lib, "user32") @@ -8903,202 +8875,204 @@ static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {} #endif //----------------------------------------------------------------------------- -// METRICS/DEBUG WINDOW +// [SECTION] METRICS/DEBUG WINDOW //----------------------------------------------------------------------------- void ImGui::ShowMetricsWindow(bool* p_open) { - if (ImGui::Begin("ImGui Metrics", p_open)) + if (!ImGui::Begin("ImGui Metrics", p_open)) { - static bool show_draw_cmd_clip_rects = true; - static bool show_window_begin_order = false; - ImGuiIO& io = ImGui::GetIO(); - ImGui::Text("Dear ImGui %s", ImGui::GetVersion()); - ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); - ImGui::Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3); - ImGui::Text("%d active windows (%d visible)", io.MetricsActiveWindows, io.MetricsRenderWindows); - ImGui::Text("%d allocations", io.MetricsActiveAllocations); - ImGui::Checkbox("Show clipping rectangles when hovering draw commands", &show_draw_cmd_clip_rects); - ImGui::Checkbox("Ctrl shows window begin order", &show_window_begin_order); + ImGui::End(); + return; + } + static bool show_draw_cmd_clip_rects = true; + static bool show_window_begin_order = false; + ImGuiIO& io = ImGui::GetIO(); + ImGui::Text("Dear ImGui %s", ImGui::GetVersion()); + ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); + ImGui::Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3); + ImGui::Text("%d active windows (%d visible)", io.MetricsActiveWindows, io.MetricsRenderWindows); + ImGui::Text("%d allocations", io.MetricsActiveAllocations); + ImGui::Checkbox("Show clipping rectangles when hovering draw commands", &show_draw_cmd_clip_rects); + ImGui::Checkbox("Ctrl shows window begin order", &show_window_begin_order); - ImGui::Separator(); + ImGui::Separator(); - struct Funcs + struct Funcs + { + static void NodeDrawList(ImGuiWindow* window, ImDrawList* draw_list, const char* label) { - static void NodeDrawList(ImGuiWindow* window, ImDrawList* draw_list, const char* label) + bool node_open = ImGui::TreeNode(draw_list, "%s: '%s' %d vtx, %d indices, %d cmds", label, draw_list->_OwnerName ? draw_list->_OwnerName : "", draw_list->VtxBuffer.Size, draw_list->IdxBuffer.Size, draw_list->CmdBuffer.Size); + if (draw_list == ImGui::GetWindowDrawList()) { - bool node_open = ImGui::TreeNode(draw_list, "%s: '%s' %d vtx, %d indices, %d cmds", label, draw_list->_OwnerName ? draw_list->_OwnerName : "", draw_list->VtxBuffer.Size, draw_list->IdxBuffer.Size, draw_list->CmdBuffer.Size); - if (draw_list == ImGui::GetWindowDrawList()) - { - ImGui::SameLine(); - ImGui::TextColored(ImColor(255,100,100), "CURRENTLY APPENDING"); // Can't display stats for active draw list! (we don't have the data double-buffered) - if (node_open) ImGui::TreePop(); - return; - } - - ImDrawList* overlay_draw_list = GetOverlayDrawList(); // Render additional visuals into the top-most draw list - if (window && IsItemHovered()) - overlay_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255)); - if (!node_open) - return; - - int elem_offset = 0; - for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.begin(); pcmd < draw_list->CmdBuffer.end(); elem_offset += pcmd->ElemCount, pcmd++) - { - if (pcmd->UserCallback == NULL && pcmd->ElemCount == 0) - continue; - if (pcmd->UserCallback) - { - ImGui::BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData); - continue; - } - ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL; - bool pcmd_node_open = ImGui::TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "Draw %4d %s vtx, tex 0x%p, clip_rect (%4.0f,%4.0f)-(%4.0f,%4.0f)", pcmd->ElemCount, draw_list->IdxBuffer.Size > 0 ? "indexed" : "non-indexed", pcmd->TextureId, pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w); - if (show_draw_cmd_clip_rects && ImGui::IsItemHovered()) - { - ImRect clip_rect = pcmd->ClipRect; - ImRect vtxs_rect; - for (int i = elem_offset; i < elem_offset + (int)pcmd->ElemCount; i++) - vtxs_rect.Add(draw_list->VtxBuffer[idx_buffer ? idx_buffer[i] : i].pos); - clip_rect.Floor(); overlay_draw_list->AddRect(clip_rect.Min, clip_rect.Max, IM_COL32(255,255,0,255)); - vtxs_rect.Floor(); overlay_draw_list->AddRect(vtxs_rect.Min, vtxs_rect.Max, IM_COL32(255,0,255,255)); - } - if (!pcmd_node_open) - continue; - - // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted. - ImGuiListClipper clipper(pcmd->ElemCount/3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible. - while (clipper.Step()) - for (int prim = clipper.DisplayStart, vtx_i = elem_offset + clipper.DisplayStart*3; prim < clipper.DisplayEnd; prim++) - { - char buf[300]; - char *buf_p = buf, *buf_end = buf + IM_ARRAYSIZE(buf); - ImVec2 triangles_pos[3]; - for (int n = 0; n < 3; n++, vtx_i++) - { - ImDrawVert& v = draw_list->VtxBuffer[idx_buffer ? idx_buffer[vtx_i] : vtx_i]; - triangles_pos[n] = v.pos; - buf_p += ImFormatString(buf_p, (int)(buf_end - buf_p), "%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n", (n == 0) ? "vtx" : " ", vtx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col); - } - ImGui::Selectable(buf, false); - if (ImGui::IsItemHovered()) - { - ImDrawListFlags backup_flags = overlay_draw_list->Flags; - overlay_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines at is more readable for very large and thin triangles. - overlay_draw_list->AddPolyline(triangles_pos, 3, IM_COL32(255,255,0,255), true, 1.0f); - overlay_draw_list->Flags = backup_flags; - } - } - ImGui::TreePop(); - } - ImGui::TreePop(); + ImGui::SameLine(); + ImGui::TextColored(ImColor(255,100,100), "CURRENTLY APPENDING"); // Can't display stats for active draw list! (we don't have the data double-buffered) + if (node_open) ImGui::TreePop(); + return; } - static void NodeWindows(ImVector& windows, const char* label) - { - if (!ImGui::TreeNode(label, "%s (%d)", label, windows.Size)) - return; - for (int i = 0; i < windows.Size; i++) - Funcs::NodeWindow(windows[i], "Window"); - ImGui::TreePop(); - } + ImDrawList* overlay_draw_list = GetOverlayDrawList(window); // Render additional visuals into the top-most draw list + if (window && IsItemHovered()) + overlay_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255)); + if (!node_open) + return; - static void NodeWindow(ImGuiWindow* window, const char* label) + int elem_offset = 0; + for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.begin(); pcmd < draw_list->CmdBuffer.end(); elem_offset += pcmd->ElemCount, pcmd++) { - if (!ImGui::TreeNode(window, "%s '%s', %d @ 0x%p", label, window->Name, window->Active || window->WasActive, window)) - return; - ImGuiWindowFlags flags = window->Flags; - NodeDrawList(window, window->DrawList, "DrawList"); - ImGui::BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), SizeContents (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->SizeContents.x, window->SizeContents.y); - ImGui::BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s..)", flags, - (flags & ImGuiWindowFlags_ChildWindow) ? "Child " : "", (flags & ImGuiWindowFlags_Tooltip) ? "Tooltip " : "", (flags & ImGuiWindowFlags_Popup) ? "Popup " : "", - (flags & ImGuiWindowFlags_Modal) ? "Modal " : "", (flags & ImGuiWindowFlags_ChildMenu) ? "ChildMenu " : "", (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "", - (flags & ImGuiWindowFlags_NoInputs) ? "NoInputs":"", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : ""); - ImGui::BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f)", window->Scroll.x, GetScrollMaxX(window), window->Scroll.y, GetScrollMaxY(window)); - ImGui::BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1); - ImGui::BulletText("Appearing: %d, Hidden: %d (Reg %d Resize %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesRegular, window->HiddenFramesForResize, window->SkipItems); - ImGui::BulletText("NavLastIds: 0x%08X,0x%08X, NavLayerActiveMask: %X", window->NavLastIds[0], window->NavLastIds[1], window->DC.NavLayerActiveMask); - ImGui::BulletText("NavLastChildNavWindow: %s", window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL"); - if (!window->NavRectRel[0].IsInverted()) - ImGui::BulletText("NavRectRel[0]: (%.1f,%.1f)(%.1f,%.1f)", window->NavRectRel[0].Min.x, window->NavRectRel[0].Min.y, window->NavRectRel[0].Max.x, window->NavRectRel[0].Max.y); - else - ImGui::BulletText("NavRectRel[0]: "); - if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow"); - if (window->ParentWindow != NULL) NodeWindow(window->ParentWindow, "ParentWindow"); - if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows"); - if (window->ColumnsStorage.Size > 0 && ImGui::TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size)) + if (pcmd->UserCallback == NULL && pcmd->ElemCount == 0) + continue; + if (pcmd->UserCallback) + { + ImGui::BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData); + continue; + } + ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL; + bool pcmd_node_open = ImGui::TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "Draw %4d %s vtx, tex 0x%p, clip_rect (%4.0f,%4.0f)-(%4.0f,%4.0f)", pcmd->ElemCount, draw_list->IdxBuffer.Size > 0 ? "indexed" : "non-indexed", pcmd->TextureId, pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w); + if (show_draw_cmd_clip_rects && ImGui::IsItemHovered()) { - for (int n = 0; n < window->ColumnsStorage.Size; n++) + ImRect clip_rect = pcmd->ClipRect; + ImRect vtxs_rect; + for (int i = elem_offset; i < elem_offset + (int)pcmd->ElemCount; i++) + vtxs_rect.Add(draw_list->VtxBuffer[idx_buffer ? idx_buffer[i] : i].pos); + clip_rect.Floor(); overlay_draw_list->AddRect(clip_rect.Min, clip_rect.Max, IM_COL32(255,255,0,255)); + vtxs_rect.Floor(); overlay_draw_list->AddRect(vtxs_rect.Min, vtxs_rect.Max, IM_COL32(255,0,255,255)); + } + if (!pcmd_node_open) + continue; + + // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted. + ImGuiListClipper clipper(pcmd->ElemCount/3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible. + while (clipper.Step()) + for (int prim = clipper.DisplayStart, vtx_i = elem_offset + clipper.DisplayStart*3; prim < clipper.DisplayEnd; prim++) { - const ImGuiColumnsSet* columns = &window->ColumnsStorage[n]; - if (ImGui::TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags)) + char buf[300]; + char *buf_p = buf, *buf_end = buf + IM_ARRAYSIZE(buf); + ImVec2 triangles_pos[3]; + for (int n = 0; n < 3; n++, vtx_i++) { - ImGui::BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->MaxX - columns->MinX, columns->MinX, columns->MaxX); - for (int column_n = 0; column_n < columns->Columns.Size; column_n++) - ImGui::BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", column_n, columns->Columns[column_n].OffsetNorm, OffsetNormToPixels(columns, columns->Columns[column_n].OffsetNorm)); - ImGui::TreePop(); + ImDrawVert& v = draw_list->VtxBuffer[idx_buffer ? idx_buffer[vtx_i] : vtx_i]; + triangles_pos[n] = v.pos; + buf_p += ImFormatString(buf_p, (int)(buf_end - buf_p), "%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n", (n == 0) ? "vtx" : " ", vtx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col); + } + ImGui::Selectable(buf, false); + if (ImGui::IsItemHovered()) + { + ImDrawListFlags backup_flags = overlay_draw_list->Flags; + overlay_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines at is more readable for very large and thin triangles. + overlay_draw_list->AddPolyline(triangles_pos, 3, IM_COL32(255,255,0,255), true, 1.0f); + overlay_draw_list->Flags = backup_flags; } } - ImGui::TreePop(); - } - ImGui::BulletText("Storage: %d bytes", window->StateStorage.Data.Size * (int)sizeof(ImGuiStorage::Pair)); ImGui::TreePop(); } - }; + ImGui::TreePop(); + } - // Access private state, we are going to display the draw lists from last frame - ImGuiContext& g = *GImGui; - Funcs::NodeWindows(g.Windows, "Windows"); - if (ImGui::TreeNode("DrawList", "Active DrawLists (%d)", g.DrawDataBuilder.Layers[0].Size)) + static void NodeWindows(ImVector& windows, const char* label) { - for (int i = 0; i < g.DrawDataBuilder.Layers[0].Size; i++) - Funcs::NodeDrawList(NULL, g.DrawDataBuilder.Layers[0][i], "DrawList"); + if (!ImGui::TreeNode(label, "%s (%d)", label, windows.Size)) + return; + for (int i = 0; i < windows.Size; i++) + Funcs::NodeWindow(windows[i], "Window"); ImGui::TreePop(); } - if (ImGui::TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size)) + + static void NodeWindow(ImGuiWindow* window, const char* label) { - for (int i = 0; i < g.OpenPopupStack.Size; i++) + if (!ImGui::TreeNode(window, "%s '%s', %d @ 0x%p", label, window->Name, window->Active || window->WasActive, window)) + return; + ImGuiWindowFlags flags = window->Flags; + NodeDrawList(window, window->DrawList, "DrawList"); + ImGui::BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), SizeContents (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->SizeContents.x, window->SizeContents.y); + ImGui::BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)", flags, + (flags & ImGuiWindowFlags_ChildWindow) ? "Child " : "", (flags & ImGuiWindowFlags_Tooltip) ? "Tooltip " : "", (flags & ImGuiWindowFlags_Popup) ? "Popup " : "", + (flags & ImGuiWindowFlags_Modal) ? "Modal " : "", (flags & ImGuiWindowFlags_ChildMenu) ? "ChildMenu " : "", (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "", + (flags & ImGuiWindowFlags_NoMouseInputs)? "NoMouseInputs":"", (flags & ImGuiWindowFlags_NoNavInputs) ? "NoNavInputs" : "", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : ""); + ImGui::BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f)", window->Scroll.x, GetWindowScrollMaxX(window), window->Scroll.y, GetWindowScrollMaxY(window)); + ImGui::BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1); + ImGui::BulletText("Appearing: %d, Hidden: %d (Reg %d Resize %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesRegular, window->HiddenFramesForResize, window->SkipItems); + ImGui::BulletText("NavLastIds: 0x%08X,0x%08X, NavLayerActiveMask: %X", window->NavLastIds[0], window->NavLastIds[1], window->DC.NavLayerActiveMask); + ImGui::BulletText("NavLastChildNavWindow: %s", window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL"); + if (!window->NavRectRel[0].IsInverted()) + ImGui::BulletText("NavRectRel[0]: (%.1f,%.1f)(%.1f,%.1f)", window->NavRectRel[0].Min.x, window->NavRectRel[0].Min.y, window->NavRectRel[0].Max.x, window->NavRectRel[0].Max.y); + else + ImGui::BulletText("NavRectRel[0]: "); + if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow"); + if (window->ParentWindow != NULL) NodeWindow(window->ParentWindow, "ParentWindow"); + if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows"); + if (window->ColumnsStorage.Size > 0 && ImGui::TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size)) { - ImGuiWindow* window = g.OpenPopupStack[i].Window; - ImGui::BulletText("PopupID: %08x, Window: '%s'%s%s", g.OpenPopupStack[i].PopupId, window ? window->Name : "NULL", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? " ChildWindow" : "", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? " ChildMenu" : ""); + for (int n = 0; n < window->ColumnsStorage.Size; n++) + { + const ImGuiColumnsSet* columns = &window->ColumnsStorage[n]; + if (ImGui::TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags)) + { + ImGui::BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->MaxX - columns->MinX, columns->MinX, columns->MaxX); + for (int column_n = 0; column_n < columns->Columns.Size; column_n++) + ImGui::BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", column_n, columns->Columns[column_n].OffsetNorm, OffsetNormToPixels(columns, columns->Columns[column_n].OffsetNorm)); + ImGui::TreePop(); + } + } + ImGui::TreePop(); } + ImGui::BulletText("Storage: %d bytes", window->StateStorage.Data.Size * (int)sizeof(ImGuiStorage::Pair)); ImGui::TreePop(); } - if (ImGui::TreeNode("Internal state")) + }; + + // Access private state, we are going to display the draw lists from last frame + ImGuiContext& g = *GImGui; + Funcs::NodeWindows(g.Windows, "Windows"); + if (ImGui::TreeNode("DrawList", "Active DrawLists (%d)", g.DrawDataBuilder.Layers[0].Size)) + { + for (int i = 0; i < g.DrawDataBuilder.Layers[0].Size; i++) + Funcs::NodeDrawList(NULL, g.DrawDataBuilder.Layers[0][i], "DrawList"); + ImGui::TreePop(); + } + if (ImGui::TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size)) + { + for (int i = 0; i < g.OpenPopupStack.Size; i++) { - const char* input_source_names[] = { "None", "Mouse", "Nav", "NavKeyboard", "NavGamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT); - ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL"); - ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL"); - ImGui::Text("HoveredId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredId, g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not - ImGui::Text("ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d, Source: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, g.ActiveIdAllowOverlap, input_source_names[g.ActiveIdSource]); - ImGui::Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL"); - ImGui::Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL"); - ImGui::Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL"); - ImGui::Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer); - ImGui::Text("NavInputSource: %s", input_source_names[g.NavInputSource]); - ImGui::Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible); - ImGui::Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId); - ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover); - ImGui::Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL"); - ImGui::Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize); - ImGui::TreePop(); + ImGuiWindow* window = g.OpenPopupStack[i].Window; + ImGui::BulletText("PopupID: %08x, Window: '%s'%s%s", g.OpenPopupStack[i].PopupId, window ? window->Name : "NULL", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? " ChildWindow" : "", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? " ChildMenu" : ""); } + ImGui::TreePop(); + } + if (ImGui::TreeNode("Internal state")) + { + const char* input_source_names[] = { "None", "Mouse", "Nav", "NavKeyboard", "NavGamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT); + ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL"); + ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL"); + ImGui::Text("HoveredId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredId, g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not + ImGui::Text("ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d, Source: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, g.ActiveIdAllowOverlap, input_source_names[g.ActiveIdSource]); + ImGui::Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL"); + ImGui::Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL"); + ImGui::Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL"); + ImGui::Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer); + ImGui::Text("NavInputSource: %s", input_source_names[g.NavInputSource]); + ImGui::Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible); + ImGui::Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId); + ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover); + ImGui::Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL"); + ImGui::Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize); + ImGui::TreePop(); + } - if (g.IO.KeyCtrl && show_window_begin_order) + if (g.IO.KeyCtrl && show_window_begin_order) + { + for (int n = 0; n < g.Windows.Size; n++) { - for (int n = 0; n < g.Windows.Size; n++) - { - ImGuiWindow* window = g.Windows[n]; - if ((window->Flags & ImGuiWindowFlags_ChildWindow) || !window->WasActive) - continue; - char buf[32]; - ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext); - float font_size = ImGui::GetFontSize() * 2; - ImDrawList* overlay_draw_list = GetOverlayDrawList(); - overlay_draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255)); - overlay_draw_list->AddText(NULL, font_size, window->Pos, IM_COL32(255, 255, 255, 255), buf); - } + ImGuiWindow* window = g.Windows[n]; + if ((window->Flags & ImGuiWindowFlags_ChildWindow) || !window->WasActive) + continue; + char buf[32]; + ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext); + float font_size = ImGui::GetFontSize() * 2; + ImDrawList* overlay_draw_list = GetOverlayDrawList(window); + overlay_draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255)); + overlay_draw_list->AddText(NULL, font_size, window->Pos, IM_COL32(255, 255, 255, 255), buf); } } ImGui::End(); diff --git a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui.h b/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui.h index a6cc137a..ff9236d9 100644 --- a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui.h +++ b/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui.h @@ -1,4 +1,4 @@ -// dear imgui, v1.64 +// dear imgui, v1.66 // (headers) // See imgui.cpp file for documentation. @@ -23,8 +23,8 @@ // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY00 then bounced up to XYY01 when release tagging happens) -#define IMGUI_VERSION "1.64" -#define IMGUI_VERSION_NUM 16401 +#define IMGUI_VERSION "1.66" +#define IMGUI_VERSION_NUM 16601 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert)) // Define attributes of all API symbols declarations (e.g. for DLL under Windows) @@ -56,14 +56,9 @@ #pragma clang diagnostic ignored "-Wold-style-cast" #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" // [Bruno Levy] #pragma clang diagnostic ignored "-Wc++11-long-long" // [Bruno Levy] -#endif - -#ifdef __GNUC__ -#pragma GCC diagnostic ignored "-Wpragmas" // [Bruno Levy] -#pragma GCC diagnostic ignored "-Wunknown-pragmas" // [Bruno Levy] -#ifndef __clang__ -#pragma GCC diagnostic ignored "-Wclass-memaccess" // [Bruno Levy] -#endif +#elif defined(__GNUC__) && __GNUC__ >= 8 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wclass-memaccess" #endif // Forward declarations @@ -196,14 +191,20 @@ namespace ImGui IMGUI_API void StyleColorsLight(ImGuiStyle* dst = NULL); // best used with borders and a custom, thicker font // Windows - // (Begin = push window to the stack and start appending to it. End = pop window from the stack. You may append multiple times to the same window during the same frame) - // Begin()/BeginChild() return false to indicate the window being collapsed or fully clipped, so you may early out and omit submitting anything to the window. - // You need to always call a matching End()/EndChild() for a Begin()/BeginChild() call, regardless of its return value (this is due to legacy reason and is inconsistent with BeginMenu/EndMenu, BeginPopup/EndPopup and other functions where the End call should only be called if the corresponding Begin function returned true.) - // Passing 'bool* p_open != NULL' shows a close widget in the upper-right corner of the window, which when clicking will set the boolean to false. - // Use child windows to introduce independent scrolling/clipping regions within a host window. Child windows can embed their own child. + // - Begin() = push window to the stack and start appending to it. End() = pop window from the stack. + // - You may append multiple times to the same window during the same frame. + // - Passing 'bool* p_open != NULL' shows a window-closing widget in the upper-right corner of the window, which clicking will set the boolean to false when clicked. + // - Begin() return false to indicate the window is collapsed or fully clipped, so you may early out and omit submitting anything to the window. + // Always call a matching End() for each Begin() call, regardless of its return value [this is due to legacy reason and is inconsistent with most other functions such as BeginMenu/EndMenu, BeginPopup/EndPopup, etc. where the EndXXX call should only be called if the corresponding BeginXXX function returned true.] IMGUI_API bool Begin(const char* name, bool* p_open = NULL, ImGuiWindowFlags flags = 0); IMGUI_API void End(); - IMGUI_API bool BeginChild(const char* str_id, const ImVec2& size = ImVec2(0,0), bool border = false, ImGuiWindowFlags flags = 0); // Begin a scrolling region. size==0.0f: use remaining window size, size<0.0f: use remaining window size minus abs(size). size>0.0f: fixed size. each axis can use a different mode, e.g. ImVec2(0,400). + + // Child Windows + // - Use child windows to begin into a self-contained independent scrolling/clipping regions within a host window. Child windows can embed their own child. + // - For each independent axis of 'size': ==0.0f: use remaining host window size / >0.0f: fixed size / <0.0f: use remaining window size minus abs(size) / Each axis can use a different mode, e.g. ImVec2(0,400). + // - BeginChild() returns false to indicate the window is collapsed or fully clipped, so you may early out and omit submitting anything to the window. + // Always call a matching EndChild() for each BeginChild() call, regardless of its return value [this is due to legacy reason and is inconsistent with most other functions such as BeginMenu/EndMenu, BeginPopup/EndPopup, etc. where the EndXXX call should only be called if the corresponding BeginXXX function returned true.] + IMGUI_API bool BeginChild(const char* str_id, const ImVec2& size = ImVec2(0,0), bool border = false, ImGuiWindowFlags flags = 0); IMGUI_API bool BeginChild(ImGuiID id, const ImVec2& size = ImVec2(0,0), bool border = false, ImGuiWindowFlags flags = 0); IMGUI_API void EndChild(); @@ -230,7 +231,7 @@ namespace ImGui IMGUI_API void SetNextWindowContentSize(const ImVec2& size); // set next window content size (~ enforce the range of scrollbars). not including window decorations (title bar, menu bar, etc.). set an axis to 0.0f to leave it automatic. call before Begin() IMGUI_API void SetNextWindowCollapsed(bool collapsed, ImGuiCond cond = 0); // set next window collapsed state. call before Begin() IMGUI_API void SetNextWindowFocus(); // set next window to be focused / front-most. call before Begin() - IMGUI_API void SetNextWindowBgAlpha(float alpha); // set next window background color alpha. helper to easily modify ImGuiCol_WindowBg/ChildBg/PopupBg. + IMGUI_API void SetNextWindowBgAlpha(float alpha); // set next window background color alpha. helper to easily modify ImGuiCol_WindowBg/ChildBg/PopupBg. you may also use ImGuiWindowFlags_NoBackground. IMGUI_API void SetWindowPos(const ImVec2& pos, ImGuiCond cond = 0); // (not recommended) set current window position - call within Begin()/End(). prefer using SetNextWindowPos(), as this may incur tearing and side-effects. IMGUI_API void SetWindowSize(const ImVec2& size, ImGuiCond cond = 0); // (not recommended) set current window size - call within Begin()/End(). set to ImVec2(0,0) to force an auto-fit. prefer using SetNextWindowSize(), as this may incur tearing and minor side-effects. IMGUI_API void SetWindowCollapsed(bool collapsed, ImGuiCond cond = 0); // (not recommended) set current window collapsed state. prefer using SetNextWindowCollapsed(). @@ -248,7 +249,7 @@ namespace ImGui IMGUI_API float GetScrollMaxY(); // get maximum scrolling amount ~~ ContentSize.Y - WindowSize.Y IMGUI_API void SetScrollX(float scroll_x); // set scrolling amount [0..GetScrollMaxX()] IMGUI_API void SetScrollY(float scroll_y); // set scrolling amount [0..GetScrollMaxY()] - IMGUI_API void SetScrollHere(float center_y_ratio = 0.5f); // adjust scrolling amount to make current cursor position visible. center_y_ratio=0.0: top, 0.5: center, 1.0: bottom. When using to make a "default/current item" visible, consider using SetItemDefaultFocus() instead. + IMGUI_API void SetScrollHereY(float center_y_ratio = 0.5f); // adjust scrolling amount to make current cursor position visible. center_y_ratio=0.0: top, 0.5: center, 1.0: bottom. When using to make a "default/current item" visible, consider using SetItemDefaultFocus() instead. IMGUI_API void SetScrollFromPosY(float pos_y, float center_y_ratio = 0.5f); // adjust scrolling amount to make given position valid. use GetCursorPos() or GetCursorStartPos()+offset to get valid positions. // Parameters stacks (shared) @@ -381,7 +382,7 @@ namespace ImGui IMGUI_API bool SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format = "%.3f", float power = 1.0f); IMGUI_API bool SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format = "%.3f", float power = 1.0f); IMGUI_API bool SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format = "%.3f", float power = 1.0f); - IMGUI_API bool SliderAngle(const char* label, float* v_rad, float v_degrees_min = -360.0f, float v_degrees_max = +360.0f); + IMGUI_API bool SliderAngle(const char* label, float* v_rad, float v_degrees_min = -360.0f, float v_degrees_max = +360.0f, const char* format = "%.0f deg"); IMGUI_API bool SliderInt(const char* label, int* v, int v_min, int v_max, const char* format = "%d"); IMGUI_API bool SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* format = "%d"); IMGUI_API bool SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* format = "%d"); @@ -393,7 +394,7 @@ namespace ImGui IMGUI_API bool VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType data_type, void* v, const void* v_min, const void* v_max, const char* format = NULL, float power = 1.0f); // Widgets: Input with Keyboard - // If you want to use InputText() with a dynamic string type such as std::string or your own, see misc/stl/imgui_stl.h + // If you want to use InputText() with a dynamic string type such as std::string or your own, see misc/cpp/imgui_stdlib.h IMGUI_API bool InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); IMGUI_API bool InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size = ImVec2(0,0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); IMGUI_API bool InputFloat(const char* label, float* v, float step = 0.0f, float step_fast = 0.0f, const char* format = "%.3f", ImGuiInputTextFlags extra_flags = 0); @@ -485,12 +486,12 @@ namespace ImGui IMGUI_API bool BeginPopupContextVoid(const char* str_id = NULL, int mouse_button = 1); // helper to open and begin popup when clicked in void (where there are no imgui windows). IMGUI_API bool BeginPopupModal(const char* name, bool* p_open = NULL, ImGuiWindowFlags flags = 0); // modal dialog (regular window with title bar, block interactions behind the modal window, can't close the modal window by clicking outside) IMGUI_API void EndPopup(); // only call EndPopup() if BeginPopupXXX() returns true! - IMGUI_API bool OpenPopupOnItemClick(const char* str_id = NULL, int mouse_button = 1); // helper to open popup when clicked on last item. return true when just opened. + IMGUI_API bool OpenPopupOnItemClick(const char* str_id = NULL, int mouse_button = 1); // helper to open popup when clicked on last item (note: actually triggers on the mouse _released_ event to be consistent with popup behaviors). return true when just opened. IMGUI_API bool IsPopupOpen(const char* str_id); // return true if the popup is open IMGUI_API void CloseCurrentPopup(); // close the popup we have begin-ed into. clicking on a MenuItem or Selectable automatically close the current popup. // Columns - // You can also use SameLine(pos_x) for simplified columns. The columns API is still work-in-progress and rather lacking. + // You can also use SameLine(pos_x) for simplified columns. The columns API is work-in-progress and rather lacking (columns are arguably the worst part of dear imgui at the moment!) IMGUI_API void Columns(int count = 1, const char* id = NULL, bool border = true); IMGUI_API void NextColumn(); // next column, defaults to current row or next row if the current row is finished IMGUI_API int GetColumnIndex(); // get current column index @@ -513,20 +514,21 @@ namespace ImGui IMGUI_API bool BeginDragDropSource(ImGuiDragDropFlags flags = 0); // call when the current item is active. If this return true, you can call SetDragDropPayload() + EndDragDropSource() IMGUI_API bool SetDragDropPayload(const char* type, const void* data, size_t size, ImGuiCond cond = 0);// type is a user defined string of maximum 32 characters. Strings starting with '_' are reserved for dear imgui internal types. Data is copied and held by imgui. IMGUI_API void EndDragDropSource(); // only call EndDragDropSource() if BeginDragDropSource() returns true! - IMGUI_API bool BeginDragDropTarget(); // call after submitting an item that may receive an item. If this returns true, you can call AcceptDragDropPayload() + EndDragDropTarget() - IMGUI_API const ImGuiPayload* AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags = 0); // accept contents of a given type. If ImGuiDragDropFlags_AcceptBeforeDelivery is set you can peek into the payload before the mouse button is released. - IMGUI_API void EndDragDropTarget(); // only call EndDragDropTarget() if BeginDragDropTarget() returns true! - + IMGUI_API bool BeginDragDropTarget(); // call after submitting an item that may receive a payload. If this returns true, you can call AcceptDragDropPayload() + EndDragDropTarget() + IMGUI_API const ImGuiPayload* AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags = 0); // accept contents of a given type. If ImGuiDragDropFlags_AcceptBeforeDelivery is set you can peek into the payload before the mouse button is released. + IMGUI_API void EndDragDropTarget(); // only call EndDragDropTarget() if BeginDragDropTarget() returns true! + IMGUI_API const ImGuiPayload* GetDragDropPayload(); // peek directly into the current payload from anywhere. may return NULL. use ImGuiPayload::IsDataType() to test for the payload type. + // Clipping IMGUI_API void PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect); IMGUI_API void PopClipRect(); // Focus, Activation - // (Prefer using "SetItemDefaultFocus()" over "if (IsWindowAppearing()) SetScrollHere()" when applicable, to make your code more forward compatible when navigation branch is merged) - IMGUI_API void SetItemDefaultFocus(); // make last item the default focused item of a window. Please use instead of "if (IsWindowAppearing()) SetScrollHere()" to signify "default item". + // (Prefer using "SetItemDefaultFocus()" over "if (IsWindowAppearing()) SetScrollHereY()" when applicable to signify "this is the default item") + IMGUI_API void SetItemDefaultFocus(); // make last item the default focused item of a window. IMGUI_API void SetKeyboardFocusHere(int offset = 0); // focus keyboard on the next widget. Use positive 'offset' to access sub components of a multiple component widget. Use -1 to access previous widget. - // Utilities + // Item/Widgets Utilities // See Demo Window under "Widgets->Querying Status" for an interactive visualization of many of those functions. IMGUI_API bool IsItemHovered(ImGuiHoveredFlags flags = 0); // is the last item hovered? (and usable, aka not blocked by a popup, etc.). See ImGuiHoveredFlags for more options. IMGUI_API bool IsItemActive(); // is the last item active? (e.g. button being held, text field being edited. This will continuously return true while holding mouse button on an item. Items that don't interact will always return false) @@ -543,6 +545,8 @@ namespace ImGui IMGUI_API ImVec2 GetItemRectMax(); // " IMGUI_API ImVec2 GetItemRectSize(); // get size of last item, in screen space IMGUI_API void SetItemAllowOverlap(); // allow last item to be overlapped by a subsequent item. sometimes useful with invisible buttons, selectables, etc. to catch unused area. + + // Miscellaneous Utilities IMGUI_API bool IsRectVisible(const ImVec2& size); // test if rectangle (of given size, starting from cursor position) is visible / not clipped. IMGUI_API bool IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max); // test if rectangle (in screen space) is visible / not clipped. to perform coarse clipping on user's side. IMGUI_API double GetTime(); @@ -554,16 +558,16 @@ namespace ImGui IMGUI_API ImGuiStorage* GetStateStorage(); IMGUI_API ImVec2 CalcTextSize(const char* text, const char* text_end = NULL, bool hide_text_after_double_hash = false, float wrap_width = -1.0f); IMGUI_API void CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end); // calculate coarse clipping for large list of evenly sized items. Prefer using the ImGuiListClipper higher-level helper if you can. - IMGUI_API bool BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags flags = 0); // helper to create a child window / scrolling region that looks like a normal widget frame IMGUI_API void EndChildFrame(); // always call EndChildFrame() regardless of BeginChildFrame() return values (which indicates a collapsed/clipped window) + // Color Utilities IMGUI_API ImVec4 ColorConvertU32ToFloat4(ImU32 in); IMGUI_API ImU32 ColorConvertFloat4ToU32(const ImVec4& in); IMGUI_API void ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v); IMGUI_API void ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b); - // Inputs + // Inputs Utilities IMGUI_API int GetKeyIndex(ImGuiKey imgui_key); // map ImGuiKey_* values into user's key index. == io.KeyMap[key] IMGUI_API bool IsKeyDown(int user_key_index); // is key being held. == io.KeysDown[user_key_index]. note that imgui doesn't know the semantic of each entry of io.KeysDown[]. Use your own indices/enums according to how your backend/engine stored them into io.KeysDown[]! IMGUI_API bool IsKeyPressed(int user_key_index, bool repeat = true); // was key pressed (went from !Down to Down). if repeat=true, uses io.KeyRepeatDelay / KeyRepeatRate @@ -618,8 +622,9 @@ enum ImGuiWindowFlags_ ImGuiWindowFlags_NoScrollWithMouse = 1 << 4, // Disable user vertically scrolling with mouse wheel. On child window, mouse wheel will be forwarded to the parent unless NoScrollbar is also set. ImGuiWindowFlags_NoCollapse = 1 << 5, // Disable user collapsing window by double-clicking on it ImGuiWindowFlags_AlwaysAutoResize = 1 << 6, // Resize every window to its content every frame + ImGuiWindowFlags_NoBackground = 1 << 7, // Disable drawing background color (WindowBg, etc.) and outside border. Similar as using SetNextWindowBgAlpha(0.0f). ImGuiWindowFlags_NoSavedSettings = 1 << 8, // Never load/save settings in .ini file - ImGuiWindowFlags_NoInputs = 1 << 9, // Disable catching mouse or keyboard inputs, hovering test with pass through. + ImGuiWindowFlags_NoMouseInputs = 1 << 9, // Disable catching mouse, hovering test with pass through. ImGuiWindowFlags_MenuBar = 1 << 10, // Has a menu-bar ImGuiWindowFlags_HorizontalScrollbar = 1 << 11, // Allow horizontal scrollbar to appear (off by default). You may use SetNextWindowContentSize(ImVec2(width,0.0f)); prior to calling Begin() to specify width. Read code in imgui_demo in the "Horizontal Scrolling" section. ImGuiWindowFlags_NoFocusOnAppearing = 1 << 12, // Disable taking focus when transitioning from hidden to visible state @@ -630,6 +635,8 @@ enum ImGuiWindowFlags_ ImGuiWindowFlags_NoNavInputs = 1 << 18, // No gamepad/keyboard navigation within the window ImGuiWindowFlags_NoNavFocus = 1 << 19, // No focusing toward this window with gamepad/keyboard navigation (e.g. skipped by CTRL+TAB) ImGuiWindowFlags_NoNav = ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus, + ImGuiWindowFlags_NoDecoration = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse, + ImGuiWindowFlags_NoInputs = ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus, // [Internal] ImGuiWindowFlags_NavFlattened = 1 << 23, // [BETA] Allow gamepad/keyboard navigation to cross over parent border to this child (only use on child that have no scrolling!) @@ -654,10 +661,10 @@ enum ImGuiInputTextFlags_ ImGuiInputTextFlags_CharsNoBlank = 1 << 3, // Filter out spaces, tabs ImGuiInputTextFlags_AutoSelectAll = 1 << 4, // Select entire text when first taking mouse focus ImGuiInputTextFlags_EnterReturnsTrue = 1 << 5, // Return 'true' when Enter is pressed (as opposed to when the value was modified) - ImGuiInputTextFlags_CallbackCompletion = 1 << 6, // Call user function on pressing TAB (for completion handling) - ImGuiInputTextFlags_CallbackHistory = 1 << 7, // Call user function on pressing Up/Down arrows (for history handling) - ImGuiInputTextFlags_CallbackAlways = 1 << 8, // Call user function every time. User code may query cursor position, modify text buffer. - ImGuiInputTextFlags_CallbackCharFilter = 1 << 9, // Call user function to filter character. Modify data->EventChar to replace/filter input, or return 1 in callback to discard character. + ImGuiInputTextFlags_CallbackCompletion = 1 << 6, // Callback on pressing TAB (for completion handling) + ImGuiInputTextFlags_CallbackHistory = 1 << 7, // Callback on pressing Up/Down arrows (for history handling) + ImGuiInputTextFlags_CallbackAlways = 1 << 8, // Callback on each iteration. User code may query cursor position, modify text buffer. + ImGuiInputTextFlags_CallbackCharFilter = 1 << 9, // Callback on character inputs to replace or discard them. Modify 'EventChar' to replace or discard, or return 1 in callback to discard. ImGuiInputTextFlags_AllowTabInput = 1 << 10, // Pressing TAB input a '\t' character into the text field ImGuiInputTextFlags_CtrlEnterForNewLine = 1 << 11, // In multi-line mode, unfocus with Enter, add new line with Ctrl+Enter (default is opposite: unfocus with Ctrl+Enter, add line with Enter). ImGuiInputTextFlags_NoHorizontalScroll = 1 << 12, // Disable following the cursor horizontally @@ -666,7 +673,7 @@ enum ImGuiInputTextFlags_ ImGuiInputTextFlags_Password = 1 << 15, // Password mode, display all characters as '*' ImGuiInputTextFlags_NoUndoRedo = 1 << 16, // Disable undo/redo. Note that input text owns the text data while active, if you want to provide your own undo/redo stack you need e.g. to call ClearActiveID(). ImGuiInputTextFlags_CharsScientific = 1 << 17, // Allow 0123456789.+-*/eE (Scientific notation input) - ImGuiInputTextFlags_CallbackResize = 1 << 18, // Allow buffer capacity resize + notify when the string wants to be resized (for string types which hold a cache of their Size) (see misc/stl/imgui_stl.h for an example of using this) + ImGuiInputTextFlags_CallbackResize = 1 << 18, // Callback on buffer capacity changes request (beyond 'buf_size' parameter value), allowing the string to grow. Notify when the string wants to be resized (for string types which hold a cache of their Size). You will be provided a new BufSize in the callback and NEED to honor it. (see misc/cpp/imgui_stdlib.h for an example of using this) // [Internal] ImGuiInputTextFlags_Multiline = 1 << 20 // For internal use by InputTextMultiline() }; @@ -727,7 +734,7 @@ enum ImGuiFocusedFlags_ ImGuiFocusedFlags_None = 0, ImGuiFocusedFlags_ChildWindows = 1 << 0, // IsWindowFocused(): Return true if any children of the window is focused ImGuiFocusedFlags_RootWindow = 1 << 1, // IsWindowFocused(): Test from root window (top most parent of the current hierarchy) - ImGuiFocusedFlags_AnyWindow = 1 << 2, // IsWindowFocused(): Return true if any window is focused + ImGuiFocusedFlags_AnyWindow = 1 << 2, // IsWindowFocused(): Return true if any window is focused. Important: If you are trying to tell how to dispatch your low-level inputs, do NOT use this. Use ImGui::GetIO().WantCaptureMouse instead. ImGuiFocusedFlags_RootAndChildWindows = ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows }; @@ -821,10 +828,10 @@ enum ImGuiKey_ ImGuiKey_COUNT }; -// [BETA] Gamepad/Keyboard directional navigation +// Gamepad/Keyboard directional navigation // Keyboard: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays. // Gamepad: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable. Back-end: set ImGuiBackendFlags_HasGamepad and fill the io.NavInputs[] fields before calling NewFrame(). Note that io.NavInputs[] is cleared by EndFrame(). -// Read instructions in imgui.cpp for more details. Download PNG/PSD at goo.gl/9LgVZW. +// Read instructions in imgui.cpp for more details. Download PNG/PSD at http://goo.gl/9LgVZW. enum ImGuiNavInput_ { // Gamepad Mapping @@ -1068,7 +1075,7 @@ struct ImGuiStyle float GrabMinSize; // Minimum width/height of a grab box for slider/scrollbar. float GrabRounding; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs. ImVec2 ButtonTextAlign; // Alignment of button text when button is larger than text. Defaults to (0.5f,0.5f) for horizontally+vertically centered. - ImVec2 DisplayWindowPadding; // Window positions are clamped to be visible within the display area by at least this amount. Only covers regular windows. + ImVec2 DisplayWindowPadding; // Window position are clamped to be visible within the display area by at least this amount. Only applies to regular windows. ImVec2 DisplaySafeAreaPadding; // If you cannot see the edges of your screen (e.g. on a TV) increase the safe area padding. Apply to popups/tooltips as well regular windows. NB: Prefer configuring your TV sets correctly! float MouseCursorScale; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later. bool AntiAliasedLines; // Enable anti-aliasing on lines/borders. Disable if you are really tight on CPU/GPU. @@ -1085,12 +1092,12 @@ struct ImGuiStyle struct ImGuiIO { //------------------------------------------------------------------ - // Settings (fill once) // Default value: + // Configuration (fill once) // Default value: //------------------------------------------------------------------ ImGuiConfigFlags ConfigFlags; // = 0 // See ImGuiConfigFlags_ enum. Set by user/application. Gamepad/keyboard navigation options, etc. ImGuiBackendFlags BackendFlags; // = 0 // Set ImGuiBackendFlags_ enum. Set by imgui_impl_xxx files or custom back-end to communicate features supported by the back-end. - ImVec2 DisplaySize; // // Display size, in pixels. For clamping windows positions. + ImVec2 DisplaySize; // // Main display size, in pixels. For clamping windows positions. float DeltaTime; // = 1.0f/60.0f // Time elapsed since last frame, in seconds. float IniSavingRate; // = 5.0f // Minimum time between saving positions/sizes to .ini file, in seconds. const char* IniFilename; // = "imgui.ini" // Path to .ini file. Set NULL to disable automatic .ini loading/saving, if e.g. you want to manually load/save from memory. @@ -1108,12 +1115,13 @@ struct ImGuiIO bool FontAllowUserScaling; // = false // Allow user scaling text of individual window with CTRL+Wheel. ImFont* FontDefault; // = NULL // Font to use on NewFrame(). Use NULL to uses Fonts->Fonts[0]. ImVec2 DisplayFramebufferScale; // = (1.0f,1.0f) // For retina display or other situations where window coordinates are different from framebuffer coordinates. User storage only, presently not used by ImGui. - ImVec2 DisplayVisibleMin; // (0.0f,0.0f) // If you use DisplaySize as a virtual space larger than your screen, set DisplayVisibleMin/Max to the visible area. - ImVec2 DisplayVisibleMax; // (0.0f,0.0f) // If the values are the same, we defaults to Min=(0.0f) and Max=DisplaySize + ImVec2 DisplayVisibleMin; // (0.0f,0.0f) // [obsolete] If you use DisplaySize as a virtual space larger than your screen, set DisplayVisibleMin/Max to the visible area. + ImVec2 DisplayVisibleMax; // (0.0f,0.0f) // [obsolete: just use io.DisplaySize] If the values are the same, we defaults to Min=(0.0f) and Max=DisplaySize // Miscellaneous configuration options + bool MouseDrawCursor; // = false // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). Cannot be easily renamed to 'io.ConfigXXX' because this is frequently used by back-end implementations. bool ConfigMacOSXBehaviors; // = defined(__APPLE__) // OS X style: Text editing cursor movement using Alt instead of Ctrl, Shortcuts using Cmd/Super instead of Ctrl, Line/Text Start and End using Cmd+Arrows instead of Home/End, Double click selects by word instead of selecting whole text, Multi-selection in lists uses Cmd/Super instead of Ctrl (was called io.OptMacOSXBehaviors prior to 1.63) - bool ConfigCursorBlink; // = true // Set to false to disable blinking cursor, for users who consider it distracting. (was called: io.OptCursorBlink prior to 1.63) + bool ConfigInputTextCursorBlink; // = true // Set to false to disable blinking cursor, for users who consider it distracting. (was called: io.OptCursorBlink prior to 1.63) bool ConfigResizeWindowsFromEdges; // = false // [BETA] Enable resizing of windows from their edges and from the lower-left corner. This requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback. (This used to be the ImGuiWindowFlags_ResizeFromAnySide flag) //------------------------------------------------------------------ @@ -1148,7 +1156,6 @@ struct ImGuiIO bool MouseDown[5]; // Mouse buttons: 0=left, 1=right, 2=middle + extras. ImGui itself mostly only uses left button (BeginPopupContext** are using right button). Others buttons allows us to track if the mouse is being used by your application + available to user as a convenience via IsMouse** API. float MouseWheel; // Mouse wheel Vertical: 1 unit scrolls about 5 lines text. float MouseWheelH; // Mouse wheel Horizontal. Most users don't have a mouse with an horizontal wheel, may not be filled by all back-ends. - bool MouseDrawCursor; // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). bool KeyCtrl; // Keyboard modifier pressed: Control bool KeyShift; // Keyboard modifier pressed: Shift bool KeyAlt; // Keyboard modifier pressed: Alt @@ -1211,6 +1218,8 @@ struct ImGuiIO #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS namespace ImGui { + // OBSOLETED in 1.66 (from Sep 2018) + static inline void SetScrollHere(float center_ratio=0.5f){ SetScrollHereY(center_ratio); } // OBSOLETED in 1.63 (from Aug 2018) static inline bool IsItemDeactivatedAfterChange() { return IsItemDeactivatedAfterEdit(); } // OBSOLETED in 1.61 (from Apr 2018) @@ -1262,7 +1271,7 @@ class ImVector inline ImVector() { Size = Capacity = 0; Data = NULL; } inline ~ImVector() { if (Data) ImGui::MemFree(Data); } inline ImVector(const ImVector& src) { Size = Capacity = 0; Data = NULL; operator=(src); } - inline ImVector& operator=(const ImVector& src) { clear(); resize(src.Size); memcpy(Data, src.Data, (size_t)Size * sizeof(value_type)); return *this; } + inline ImVector& operator=(const ImVector& src) { clear(); resize(src.Size); memcpy(Data, src.Data, (size_t)Size * sizeof(value_type)); return *this; } inline bool empty() const { return Size == 0; } inline int size() const { return Size; } @@ -1362,25 +1371,27 @@ struct ImGuiTextFilter int CountGrep; }; -// Helper: Text buffer for logging/accumulating text +// Helper: Growable text buffer for logging/accumulating text +// (this could be called 'ImGuiTextBuilder' / 'ImGuiStringBuilder') struct ImGuiTextBuffer { ImVector Buf; - - ImGuiTextBuffer() { Buf.push_back(0); } - inline char operator[](int i) { return Buf.Data[i]; } - const char* begin() const { return &Buf.front(); } - const char* end() const { return &Buf.back(); } // Buf is zero-terminated, so end() will point on the zero-terminator - int size() const { return Buf.Size - 1; } - bool empty() { return Buf.Size <= 1; } - void clear() { Buf.clear(); Buf.push_back(0); } - void reserve(int capacity) { Buf.reserve(capacity); } - const char* c_str() const { return Buf.Data; } + static char EmptyString[1]; + + ImGuiTextBuffer() { } + inline char operator[](int i) { IM_ASSERT(Buf.Data != NULL); return Buf.Data[i]; } + const char* begin() const { return Buf.Data ? &Buf.front() : EmptyString; } + const char* end() const { return Buf.Data ? &Buf.back() : EmptyString; } // Buf is zero-terminated, so end() will point on the zero-terminator + int size() const { return Buf.Data ? Buf.Size - 1 : 0; } + bool empty() { return Buf.Size <= 1; } + void clear() { Buf.clear(); } + void reserve(int capacity) { Buf.reserve(capacity); } + const char* c_str() const { return Buf.Data ? Buf.Data : EmptyString; } IMGUI_API void appendf(const char* fmt, ...) IM_FMTARGS(2); IMGUI_API void appendfv(const char* fmt, va_list args) IM_FMTLIST(2); }; -// Helper: key->value storage +// Helper: Key->Value storage // Typically you don't have to worry about this since a storage is held within each Window. // We use it to e.g. store collapse state for a tree (Int 0/1) // This is optimized for efficient lookup (dichotomy into a contiguous buffer) and rare insertion (typically tied to user interactions aka max once a frame) @@ -1431,9 +1442,12 @@ struct ImGuiStorage // Shared state of InputText(), passed as an argument to your callback when a ImGuiInputTextFlags_Callback* flag is used. // The callback function should return 0 by default. -// Special processing: -// - ImGuiInputTextFlags_CallbackCharFilter: return 1 if the character is not allowed. You may also set 'EventChar=0' as any character replacement are allowed. -// - ImGuiInputTextFlags_CallbackResize: notified by InputText() when the string is resized. BufTextLen is set to the new desired string length so you can update the string size on your side of the fence. You can also replace Buf pointer if your underlying data is reallocated. No need to initialize new characters or zero-terminator as InputText will do it right after the resize callback. +// Callbacks (follow a flag name and see comments in ImGuiInputTextFlags_ declarations for more details) +// - ImGuiInputTextFlags_CallbackCompletion: Callback on pressing TAB +// - ImGuiInputTextFlags_CallbackHistory: Callback on pressing Up/Down arrows +// - ImGuiInputTextFlags_CallbackAlways: Callback on each iteration +// - ImGuiInputTextFlags_CallbackCharFilter: Callback on character inputs to replace or discard them. Modify 'EventChar' to replace or discard, or return 1 in callback to discard. +// - ImGuiInputTextFlags_CallbackResize: Callback on buffer capacity changes request (beyond 'buf_size' parameter value), allowing the string to grow. struct ImGuiInputTextCallbackData { ImGuiInputTextFlags EventFlag; // One ImGuiInputTextFlags_Callback* // Read-only @@ -1443,19 +1457,19 @@ struct ImGuiInputTextCallbackData // Arguments for the different callback events // - To modify the text buffer in a callback, prefer using the InsertChars() / DeleteChars() function. InsertChars() will take care of calling the resize callback if necessary. // - If you know your edits are not going to resize the underlying buffer allocation, you may modify the contents of 'Buf[]' directly. You need to update 'BufTextLen' accordingly (0 <= BufTextLen < BufSize) and set 'BufDirty'' to true so InputText can update its internal state. - ImWchar EventChar; // Character input // Read-write // [CharFilter] Replace character or set to zero. return 1 is equivalent to setting EventChar=0; + ImWchar EventChar; // Character input // Read-write // [CharFilter] Replace character with another one, or set to zero to drop. return 1 is equivalent to setting EventChar=0; ImGuiKey EventKey; // Key pressed (Up/Down/TAB) // Read-only // [Completion,History] char* Buf; // Text buffer // Read-write // [Resize] Can replace pointer / [Completion,History,Always] Only write to pointed data, don't replace the actual pointer! - int BufTextLen; // Text length in bytes // Read-write // [Resize,Completion,History,Always] Exclude zero-terminator storage. In C land: == strlen(some_text), in C++ land: string.length() - int BufSize; // Buffer capacity in bytes // Read-only // [Resize,Completion,History,Always] Include zero-terminator storage. In C land == ARRAYSIZE(my_char_array), in C++ land: string.capacity()+1 - bool BufDirty; // Set if you modify Buf/BufTextLen!! // Write // [Completion,History,Always] + int BufTextLen; // Text length (in bytes) // Read-write // [Resize,Completion,History,Always] Exclude zero-terminator storage. In C land: == strlen(some_text), in C++ land: string.length() + int BufSize; // Buffer size (in bytes) = capacity+1 // Read-only // [Resize,Completion,History,Always] Include zero-terminator storage. In C land == ARRAYSIZE(my_char_array), in C++ land: string.capacity()+1 + bool BufDirty; // Set if you modify Buf/BufTextLen! // Write // [Completion,History,Always] int CursorPos; // // Read-write // [Completion,History,Always] int SelectionStart; // // Read-write // [Completion,History,Always] == to SelectionEnd when no selection) int SelectionEnd; // // Read-write // [Completion,History,Always] // Helper functions for text manipulation. // Use those function to benefit from the CallbackResize behaviors. Calling those function reset the selection. - ImGuiInputTextCallbackData(); + IMGUI_API ImGuiInputTextCallbackData(); IMGUI_API void DeleteChars(int pos, int bytes_count); IMGUI_API void InsertChars(int pos, const char* text, const char* text_end = NULL); bool HasSelection() const { return SelectionStart != SelectionEnd; } @@ -1588,7 +1602,7 @@ struct ImDrawCmd ImDrawCallback UserCallback; // If != NULL, call the function instead of rendering the vertices. clip_rect and texture_id will be set normally. void* UserCallbackData; // The draw callback code can access this. - ImDrawCmd() { ElemCount = 0; ClipRect.x = ClipRect.y = ClipRect.z = ClipRect.w = 0.0f; TextureId = NULL; UserCallback = NULL; UserCallbackData = NULL; } + ImDrawCmd() { ElemCount = 0; ClipRect.x = ClipRect.y = ClipRect.z = ClipRect.w = 0.0f; TextureId = (ImTextureID)NULL; UserCallback = NULL; UserCallbackData = NULL; } }; // Vertex index (override with '#define ImDrawIdx unsigned int' inside in imconfig.h) @@ -1860,7 +1874,7 @@ struct ImFontAtlas ImVector UsedChars; // Store 1-bit per Unicode code point (0=unused, 1=used) GlyphRangesBuilder() { UsedChars.resize(0x10000 / 8); memset(UsedChars.Data, 0, 0x10000 / 8); } bool GetBit(int n) const { return (UsedChars[n >> 3] & (1 << (n & 7))) != 0; } - void SetBit(int n) { UsedChars[n >> 3] |= (unsigned char) (1 << (n & 7)); } // Set bit 'c' in the array [Bruno Levy] added cast to silent warning + void SetBit(int n) { UsedChars[n >> 3] |= (unsigned char) (1 << (n & 7)); } //Set bit 'c' in the array [Bruno Levy] added cast to silence warning void AddChar(ImWchar c) { SetBit(c); } // Add character IMGUI_API void AddText(const char* text, const char* text_end = NULL); // Add string (each character of the UTF-8 string are added) IMGUI_API void AddRanges(const ImWchar* ranges); // Add ranges, e.g. builder.AddRanges(ImFontAtlas::GetGlyphRangesDefault()) to force add all of ASCII/Latin+Ext @@ -1927,7 +1941,7 @@ struct ImFont ImVec2 DisplayOffset; // = (0.f,0.f) // Offset font rendering by xx pixels ImVector Glyphs; // // All glyphs. ImVector IndexAdvanceX; // // Sparse. Glyphs->AdvanceX in a directly indexable way (more cache-friendly, for CalcTextSize functions which are often bottleneck in large UI). - ImVector IndexLookup; // // Sparse. Index glyphs by Unicode code-point. + ImVector IndexLookup; // // Sparse. Index glyphs by Unicode code-point. const ImFontGlyph* FallbackGlyph; // == FindGlyph(FontFallbackChar) float FallbackAdvanceX; // == FallbackGlyph->AdvanceX ImWchar FallbackChar; // = '?' // Replacement glyph if one isn't found. Only set via SetFallbackChar() @@ -1956,7 +1970,7 @@ struct ImFont // 'wrap_width' enable automatic word-wrapping across multiple lines to fit into given width. 0.0f to disable. IMGUI_API ImVec2 CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end = NULL, const char** remaining = NULL) const; // utf8 IMGUI_API const char* CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) const; - IMGUI_API void RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, unsigned short c) const; + IMGUI_API void RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, ImWchar c) const; IMGUI_API void RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width = 0.0f, bool cpu_fine_clip = false) const; // [Internal] diff --git a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_demo.cpp b/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_demo.cpp index 1702474e..b7f53104 100644 --- a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_demo.cpp +++ b/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_demo.cpp @@ -1,22 +1,46 @@ -// dear imgui, v1.64 +// dear imgui, v1.66 // (demo code) -// Message to the person tempted to delete this file when integrating ImGui into their code base: -// Don't do it! Do NOT remove this file from your project! It is useful reference code that you and other users will want to refer to. +// Message to the person tempted to delete this file when integrating Dear ImGui into their code base: +// Do NOT remove this file from your project! Think again! It is the most useful reference code that you and other coders +// will want to refer to and call. Have the ImGui::ShowDemoWindow() function wired in an always-available debug menu of +// your game/app! Removing this file from your project is hindering access to documentation for everyone in your team, +// likely leading you to poorer usage of the library. // Everything in this file will be stripped out by the linker if you don't call ImGui::ShowDemoWindow(). -// During development, you can call ImGui::ShowDemoWindow() in your code to learn about various features of ImGui. Have it wired in a debug menu! -// Removing this file from your project is hindering access to documentation for everyone in your team, likely leading you to poorer usage of the library. -// Note that you can #define IMGUI_DISABLE_DEMO_WINDOWS in imconfig.h for the same effect. -// If you want to link core ImGui in your final builds but not those demo windows, #define IMGUI_DISABLE_DEMO_WINDOWS in imconfig.h and those functions will be empty. -// In other situation, when you have ImGui available you probably want this to be available for reference and execution. +// If you want to link core Dear ImGui in your shipped builds but want an easy guarantee that the demo will not be linked, +// you can setup your imconfig.h with #define IMGUI_DISABLE_DEMO_WINDOWS and those functions will be empty. +// In other situation, whenever you have Dear ImGui available you probably want this to be available for reference. // Thank you, // -Your beloved friend, imgui_demo.cpp (that you won't delete) -// Message to beginner C/C++ programmers about the meaning of the 'static' keyword: in this demo code, we frequently we use 'static' variables inside functions. -// A static variable persist across calls, so it is essentially like a global variable but declared inside the scope of the function. -// We do this as a way to gather code and data in the same place, just to make the demo code faster to read, faster to write, and use less code. -// It also happens to be a convenient way of storing simple UI related information as long as your function doesn't need to be reentrant or used in threads. -// This might be a pattern you occasionally want to use in your code, but most of the real data you would be editing is likely to be stored outside your functions. +// Message to beginner C/C++ programmers about the meaning of the 'static' keyword: +// In this demo code, we frequently we use 'static' variables inside functions. A static variable persist across calls, so it is +// essentially like a global variable but declared inside the scope of the function. We do this as a way to gather code and data +// in the same place, to make the demo source code faster to read, faster to write, and smaller in size. +// It also happens to be a convenient way of storing simple UI related information as long as your function doesn't need to be reentrant +// or used in threads. This might be a pattern you will want to use in your code, but most of the real data you would be editing is +// likely going to be stored outside your functions. + +/* + +Index of this file: + +// [SECTION] Forward Declarations, Helpers +// [SECTION] Demo Window / ShowDemoWindow() +// [SECTION] Style Editor / ShowStyleEditor() +// [SECTION] Example App: Main Menu Bar / ShowExampleAppMainMenuBar() +// [SECTION] Example App: Debug Console / ShowExampleAppConsole() +// [SECTION] Example App: Debug Log / ShowExampleAppLog() +// [SECTION] Example App: Simple Layout / ShowExampleAppLayout() +// [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor() +// [SECTION] Example App: Long Text / ShowExampleAppLongText() +// [SECTION] Example App: Auto Resize / ShowExampleAppAutoResize() +// [SECTION] Example App: Constrained Resize / ShowExampleAppConstrainedResize() +// [SECTION] Example App: Simple Overlay / ShowExampleAppSimpleOverlay() +// [SECTION] Example App: Manipulating Window Titles / ShowExampleAppWindowTitles() +// [SECTION] Example App: Custom Rendering using ImDrawList API / ShowExampleAppCustomRendering() + +*/ #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) #define _CRT_SECURE_NO_WARNINGS @@ -67,7 +91,7 @@ #define IM_MAX(_A,_B) (((_A) >= (_B)) ? (_A) : (_B)) //----------------------------------------------------------------------------- -// DEMO CODE +// [SECTION] Forward Declarations, Helpers //----------------------------------------------------------------------------- #if !defined(IMGUI_DISABLE_OBSOLETE_FUNCTIONS) && defined(IMGUI_DISABLE_TEST_WINDOWS) && !defined(IMGUI_DISABLE_DEMO_WINDOWS) // Obsolete name since 1.53, TEST->DEMO @@ -127,6 +151,17 @@ void ImGui::ShowUserGuide() ImGui::Unindent(); } +//----------------------------------------------------------------------------- +// [SECTION] Demo Window / ShowDemoWindow() +//----------------------------------------------------------------------------- + +// We split the contents of the big ShowDemoWindow() function into smaller functions (because the link time of very large functions grow non-linearly) +static void ShowDemoWindowWidgets(); +static void ShowDemoWindowLayout(); +static void ShowDemoWindowPopups(); +static void ShowDemoWindowColumns(); +static void ShowDemoWindowMisc(); + // Demonstrate most Dear ImGui features (this is big function!) // You may execute this function to experiment with the UI and understand what it does. You may then search for keywords in the code when you are interested by a specific feature. void ImGui::ShowDemoWindow(bool* p_open) @@ -182,16 +217,20 @@ void ImGui::ShowDemoWindow(bool* p_open) static bool no_collapse = false; static bool no_close = false; static bool no_nav = false; + static bool no_background = false; + static bool no_bring_to_front = false; ImGuiWindowFlags window_flags = 0; - if (no_titlebar) window_flags |= ImGuiWindowFlags_NoTitleBar; - if (no_scrollbar) window_flags |= ImGuiWindowFlags_NoScrollbar; - if (!no_menu) window_flags |= ImGuiWindowFlags_MenuBar; - if (no_move) window_flags |= ImGuiWindowFlags_NoMove; - if (no_resize) window_flags |= ImGuiWindowFlags_NoResize; - if (no_collapse) window_flags |= ImGuiWindowFlags_NoCollapse; - if (no_nav) window_flags |= ImGuiWindowFlags_NoNav; - if (no_close) p_open = NULL; // Don't pass our bool* to Begin + if (no_titlebar) window_flags |= ImGuiWindowFlags_NoTitleBar; + if (no_scrollbar) window_flags |= ImGuiWindowFlags_NoScrollbar; + if (!no_menu) window_flags |= ImGuiWindowFlags_MenuBar; + if (no_move) window_flags |= ImGuiWindowFlags_NoMove; + if (no_resize) window_flags |= ImGuiWindowFlags_NoResize; + if (no_collapse) window_flags |= ImGuiWindowFlags_NoCollapse; + if (no_nav) window_flags |= ImGuiWindowFlags_NoNav; + if (no_background) window_flags |= ImGuiWindowFlags_NoBackground; + if (no_bring_to_front) window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus; + if (no_close) p_open = NULL; // Don't pass our bool* to Begin // We specify a default position/size in case there's no data in the .ini file. Typically this isn't required! We only do it to make the Demo applications a little more welcoming. ImGui::SetNextWindowPos(ImVec2(650, 20), ImGuiCond_FirstUseEver); @@ -246,1570 +285,1691 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::Spacing(); if (ImGui::CollapsingHeader("Help")) { - ImGui::TextWrapped("This window is being created by the ShowDemoWindow() function. Please refer to the code in imgui_demo.cpp for reference.\n\n"); + ImGui::Text("PROGRAMMER GUIDE:"); + ImGui::BulletText("Please see the ShowDemoWindow() code in imgui_demo.cpp. <- you are here!"); + ImGui::BulletText("Please see the comments in imgui.cpp."); + ImGui::BulletText("Please see the examples/ in application."); + ImGui::BulletText("Enable 'io.ConfigFlags |= NavEnableKeyboard' for keyboard controls."); + ImGui::BulletText("Enable 'io.ConfigFlags |= NavEnableGamepad' for gamepad controls."); + ImGui::Separator(); + ImGui::Text("USER GUIDE:"); ImGui::ShowUserGuide(); } - if (ImGui::CollapsingHeader("Window options")) + if (ImGui::CollapsingHeader("Configuration")) { - ImGui::Checkbox("No titlebar", &no_titlebar); ImGui::SameLine(150); - ImGui::Checkbox("No scrollbar", &no_scrollbar); ImGui::SameLine(300); - ImGui::Checkbox("No menu", &no_menu); - ImGui::Checkbox("No move", &no_move); ImGui::SameLine(150); - ImGui::Checkbox("No resize", &no_resize); ImGui::SameLine(300); - ImGui::Checkbox("No collapse", &no_collapse); - ImGui::Checkbox("No close", &no_close); ImGui::SameLine(150); - ImGui::Checkbox("No nav", &no_nav); + ImGuiIO& io = ImGui::GetIO(); + + if (ImGui::TreeNode("Configuration##2")) + { + ImGui::CheckboxFlags("io.ConfigFlags: NavEnableKeyboard", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NavEnableKeyboard); + ImGui::CheckboxFlags("io.ConfigFlags: NavEnableGamepad", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NavEnableGamepad); + ImGui::SameLine(); ShowHelpMarker("Required back-end to feed in gamepad inputs in io.NavInputs[] and set io.BackendFlags |= ImGuiBackendFlags_HasGamepad.\n\nRead instructions in imgui.cpp for details."); + ImGui::CheckboxFlags("io.ConfigFlags: NavEnableSetMousePos", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NavEnableSetMousePos); + ImGui::SameLine(); ShowHelpMarker("Instruct navigation to move the mouse cursor. See comment for ImGuiConfigFlags_NavEnableSetMousePos."); + ImGui::CheckboxFlags("io.ConfigFlags: NoMouse", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NoMouse); + if (io.ConfigFlags & ImGuiConfigFlags_NoMouse) // Create a way to restore this flag otherwise we could be stuck completely! + { + if (fmodf((float)ImGui::GetTime(), 0.40f) < 0.20f) + { + ImGui::SameLine(); + ImGui::Text("<>"); + } + if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Space))) + io.ConfigFlags &= ~ImGuiConfigFlags_NoMouse; + } + ImGui::CheckboxFlags("io.ConfigFlags: NoMouseCursorChange", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NoMouseCursorChange); + ImGui::SameLine(); ShowHelpMarker("Instruct back-end to not alter mouse cursor shape and visibility."); + ImGui::Checkbox("io.ConfigInputTextCursorBlink", &io.ConfigInputTextCursorBlink); + ImGui::SameLine(); ShowHelpMarker("Set to false to disable blinking cursor, for users who consider it distracting"); + ImGui::Checkbox("io.ConfigResizeWindowsFromEdges [beta]", &io.ConfigResizeWindowsFromEdges); + ImGui::SameLine(); ShowHelpMarker("Enable resizing of windows from their edges and from the lower-left corner.\nThis requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback."); + ImGui::Checkbox("io.MouseDrawCursor", &io.MouseDrawCursor); + ImGui::SameLine(); ShowHelpMarker("Instruct Dear ImGui to render a mouse cursor for you. Note that a mouse cursor rendered via your application GPU rendering path will feel more laggy than hardware cursor, but will be more in sync with your other visuals.\n\nSome desktop applications may use both kinds of cursors (e.g. enable software cursor only when resizing/dragging something)."); + ImGui::TreePop(); + ImGui::Separator(); + } + + if (ImGui::TreeNode("Backend Flags")) + { + ImGuiBackendFlags backend_flags = io.BackendFlags; // Make a local copy to avoid modifying the back-end flags. + ImGui::CheckboxFlags("io.BackendFlags: HasGamepad", (unsigned int *)&backend_flags, ImGuiBackendFlags_HasGamepad); + ImGui::CheckboxFlags("io.BackendFlags: HasMouseCursors", (unsigned int *)&backend_flags, ImGuiBackendFlags_HasMouseCursors); + ImGui::CheckboxFlags("io.BackendFlags: HasSetMousePos", (unsigned int *)&backend_flags, ImGuiBackendFlags_HasSetMousePos); + ImGui::TreePop(); + ImGui::Separator(); + } if (ImGui::TreeNode("Style")) { ImGui::ShowStyleEditor(); ImGui::TreePop(); + ImGui::Separator(); } if (ImGui::TreeNode("Capture/Logging")) { - ImGui::TextWrapped("The logging API redirects all text output so you can easily capture the content of a window or a block. Tree nodes can be automatically expanded. You can also call ImGui::LogText() to output directly to the log without a visual output."); + ImGui::TextWrapped("The logging API redirects all text output so you can easily capture the content of a window or a block. Tree nodes can be automatically expanded."); + ShowHelpMarker("Try opening any of the contents below in this window and then click one of the \"Log To\" button."); ImGui::LogButtons(); + ImGui::TextWrapped("You can also call ImGui::LogText() to output directly to the log without a visual output."); + if (ImGui::Button("Copy \"Hello, world!\" to clipboard")) + { + ImGui::LogToClipboard(); + ImGui::LogText("Hello, world!"); + ImGui::LogFinish(); + } ImGui::TreePop(); } } - if (ImGui::CollapsingHeader("Widgets")) + if (ImGui::CollapsingHeader("Window options")) { - if (ImGui::TreeNode("Basic")) + ImGui::Checkbox("No titlebar", &no_titlebar); ImGui::SameLine(150); + ImGui::Checkbox("No scrollbar", &no_scrollbar); ImGui::SameLine(300); + ImGui::Checkbox("No menu", &no_menu); + ImGui::Checkbox("No move", &no_move); ImGui::SameLine(150); + ImGui::Checkbox("No resize", &no_resize); ImGui::SameLine(300); + ImGui::Checkbox("No collapse", &no_collapse); + ImGui::Checkbox("No close", &no_close); ImGui::SameLine(150); + ImGui::Checkbox("No nav", &no_nav); ImGui::SameLine(300); + ImGui::Checkbox("No background", &no_background); + ImGui::Checkbox("No bring to front", &no_bring_to_front); + } + + // All demo contents + ShowDemoWindowWidgets(); + ShowDemoWindowLayout(); + ShowDemoWindowPopups(); + ShowDemoWindowColumns(); + ShowDemoWindowMisc(); + + // End of ShowDemoWindow() + ImGui::End(); +} + +static void ShowDemoWindowWidgets() +{ + if (!ImGui::CollapsingHeader("Widgets")) + return; + + if (ImGui::TreeNode("Basic")) + { + static int clicked = 0; + if (ImGui::Button("Button")) + clicked++; + if (clicked & 1) { - static int clicked = 0; - if (ImGui::Button("Button")) - clicked++; - if (clicked & 1) - { + ImGui::SameLine(); + ImGui::Text("Thanks for clicking me!"); + } + + static bool check = true; + ImGui::Checkbox("checkbox", &check); + + static int e = 0; + ImGui::RadioButton("radio a", &e, 0); ImGui::SameLine(); + ImGui::RadioButton("radio b", &e, 1); ImGui::SameLine(); + ImGui::RadioButton("radio c", &e, 2); + + // Color buttons, demonstrate using PushID() to add unique identifier in the ID stack, and changing style. + for (int i = 0; i < 7; i++) + { + if (i > 0) ImGui::SameLine(); - ImGui::Text("Thanks for clicking me!"); - } + ImGui::PushID(i); + ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(i/7.0f, 0.6f, 0.6f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(i/7.0f, 0.7f, 0.7f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(i/7.0f, 0.8f, 0.8f)); + ImGui::Button("Click"); + ImGui::PopStyleColor(3); + ImGui::PopID(); + } - static bool check = true; - ImGui::Checkbox("checkbox", &check); + // Use AlignTextToFramePadding() to align text baseline to the baseline of framed elements (otherwise a Text+SameLine+Button sequence will have the text a little too high by default) + ImGui::AlignTextToFramePadding(); + ImGui::Text("Hold to repeat:"); + ImGui::SameLine(); - static int e = 0; - ImGui::RadioButton("radio a", &e, 0); ImGui::SameLine(); - ImGui::RadioButton("radio b", &e, 1); ImGui::SameLine(); - ImGui::RadioButton("radio c", &e, 2); + // Arrow buttons with Repeater + static int counter = 0; + float spacing = ImGui::GetStyle().ItemInnerSpacing.x; + ImGui::PushButtonRepeat(true); + if (ImGui::ArrowButton("##left", ImGuiDir_Left)) { counter--; } + ImGui::SameLine(0.0f, spacing); + if (ImGui::ArrowButton("##right", ImGuiDir_Right)) { counter++; } + ImGui::PopButtonRepeat(); + ImGui::SameLine(); + ImGui::Text("%d", counter); - // Color buttons, demonstrate using PushID() to add unique identifier in the ID stack, and changing style. - for (int i = 0; i < 7; i++) - { - if (i > 0) ImGui::SameLine(); - ImGui::PushID(i); - ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(i/7.0f, 0.6f, 0.6f)); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(i/7.0f, 0.7f, 0.7f)); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(i/7.0f, 0.8f, 0.8f)); - ImGui::Button("Click"); - ImGui::PopStyleColor(3); - ImGui::PopID(); - } + ImGui::Text("Hover over me"); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("I am a tooltip"); - // Arrow buttons - static int counter = 0; - float spacing = ImGui::GetStyle().ItemInnerSpacing.x; - ImGui::PushButtonRepeat(true); - if (ImGui::ArrowButton("##left", ImGuiDir_Left)) { counter--; } - ImGui::SameLine(0.0f, spacing); - if (ImGui::ArrowButton("##right", ImGuiDir_Right)) { counter++; } - ImGui::PopButtonRepeat(); - ImGui::SameLine(); - ImGui::Text("%d", counter); + ImGui::SameLine(); + ImGui::Text("- or me"); + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::Text("I am a fancy tooltip"); + static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f }; + ImGui::PlotLines("Curve", arr, IM_ARRAYSIZE(arr)); + ImGui::EndTooltip(); + } - ImGui::Text("Hover over me"); - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("I am a tooltip"); + ImGui::Separator(); - ImGui::SameLine(); - ImGui::Text("- or me"); - if (ImGui::IsItemHovered()) - { - ImGui::BeginTooltip(); - ImGui::Text("I am a fancy tooltip"); - static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f }; - ImGui::PlotLines("Curve", arr, IM_ARRAYSIZE(arr)); - ImGui::EndTooltip(); - } + ImGui::LabelText("label", "Value"); - ImGui::Separator(); + { + // Using the _simplified_ one-liner Combo() api here + // See "Combo" section for examples of how to use the more complete BeginCombo()/EndCombo() api. + const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLLLLL", "MMMM", "OOOOOOO" }; + static int item_current = 0; + ImGui::Combo("combo", &item_current, items, IM_ARRAYSIZE(items)); + ImGui::SameLine(); ShowHelpMarker("Refer to the \"Combo\" section below for an explanation of the full BeginCombo/EndCombo API, and demonstration of various flags.\n"); + } - ImGui::LabelText("label", "Value"); + { + static char str0[128] = "Hello, world!"; + static int i0 = 123; + ImGui::InputText("input text", str0, IM_ARRAYSIZE(str0)); + ImGui::SameLine(); ShowHelpMarker("USER:\nHold SHIFT or use mouse to select text.\n" "CTRL+Left/Right to word jump.\n" "CTRL+A or double-click to select all.\n" "CTRL+X,CTRL+C,CTRL+V clipboard.\n" "CTRL+Z,CTRL+Y undo/redo.\n" "ESCAPE to revert.\n\nPROGRAMMER:\nYou can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputText() to a dynamic string type. See misc/cpp/imgui_stdlib.h for an example (this is not demonstrated in imgui_demo.cpp)."); - { - // Using the _simplified_ one-liner Combo() api here - // See "Combo" section for examples of how to use the more complete BeginCombo()/EndCombo() api. - const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLLLLL", "MMMM", "OOOOOOO" }; - static int item_current = 0; - ImGui::Combo("combo", &item_current, items, IM_ARRAYSIZE(items)); - ImGui::SameLine(); ShowHelpMarker("Refer to the \"Combo\" section below for an explanation of the full BeginCombo/EndCombo API, and demonstration of various flags.\n"); - } + ImGui::InputInt("input int", &i0); + ImGui::SameLine(); ShowHelpMarker("You can apply arithmetic operators +,*,/ on numerical values.\n e.g. [ 100 ], input \'*2\', result becomes [ 200 ]\nUse +- to subtract.\n"); - { - static char str0[128] = "Hello, world!"; - static int i0 = 123; - ImGui::InputText("input text", str0, IM_ARRAYSIZE(str0)); - ImGui::SameLine(); ShowHelpMarker("USER:\nHold SHIFT or use mouse to select text.\n" "CTRL+Left/Right to word jump.\n" "CTRL+A or double-click to select all.\n" "CTRL+X,CTRL+C,CTRL+V clipboard.\n" "CTRL+Z,CTRL+Y undo/redo.\n" "ESCAPE to revert.\n\nPROGRAMMER:\nYou can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputText() to a dynamic string type. See misc/stl/imgui_stl.h for an example (this is not demonstrated in imgui_demo.cpp)."); + static float f0 = 0.001f; + ImGui::InputFloat("input float", &f0, 0.01f, 1.0f); - ImGui::InputInt("input int", &i0); - ImGui::SameLine(); ShowHelpMarker("You can apply arithmetic operators +,*,/ on numerical values.\n e.g. [ 100 ], input \'*2\', result becomes [ 200 ]\nUse +- to subtract.\n"); + static double d0 = 999999.00000001; + ImGui::InputDouble("input double", &d0, 0.01f, 1.0f, "%.8f"); - static float f0 = 0.001f; - ImGui::InputFloat("input float", &f0, 0.01f, 1.0f); + static float f1 = 1.e10f; + ImGui::InputFloat("input scientific", &f1, 0.0f, 0.0f, "%e"); + ImGui::SameLine(); ShowHelpMarker("You can input value using the scientific notation,\n e.g. \"1e+8\" becomes \"100000000\".\n"); - static double d0 = 999999.00000001; - ImGui::InputDouble("input double", &d0, 0.01f, 1.0f, "%.8f"); + static float vec4a[4] = { 0.10f, 0.20f, 0.30f, 0.44f }; + ImGui::InputFloat3("input float3", vec4a); + } - static float f1 = 1.e10f; - ImGui::InputFloat("input scientific", &f1, 0.0f, 0.0f, "%e"); - ImGui::SameLine(); ShowHelpMarker("You can input value using the scientific notation,\n e.g. \"1e+8\" becomes \"100000000\".\n"); + { + static int i1 = 50, i2 = 42; + ImGui::DragInt("drag int", &i1, 1); + ImGui::SameLine(); ShowHelpMarker("Click and drag to edit value.\nHold SHIFT/ALT for faster/slower edit.\nDouble-click or CTRL+click to input value."); - static float vec4a[4] = { 0.10f, 0.20f, 0.30f, 0.44f }; - ImGui::InputFloat3("input float3", vec4a); - } + ImGui::DragInt("drag int 0..100", &i2, 1, 0, 100, "%d%%"); - { - static int i1 = 50, i2 = 42; - ImGui::DragInt("drag int", &i1, 1); - ImGui::SameLine(); ShowHelpMarker("Click and drag to edit value.\nHold SHIFT/ALT for faster/slower edit.\nDouble-click or CTRL+click to input value."); + static float f1=1.00f, f2=0.0067f; + ImGui::DragFloat("drag float", &f1, 0.005f); + ImGui::DragFloat("drag small float", &f2, 0.0001f, 0.0f, 0.0f, "%.06f ns"); + } - ImGui::DragInt("drag int 0..100", &i2, 1, 0, 100, "%d%%"); + { + static int i1=0; + ImGui::SliderInt("slider int", &i1, -1, 3); + ImGui::SameLine(); ShowHelpMarker("CTRL+click to input value."); - static float f1=1.00f, f2=0.0067f; - ImGui::DragFloat("drag float", &f1, 0.005f); - ImGui::DragFloat("drag small float", &f2, 0.0001f, 0.0f, 0.0f, "%.06f ns"); - } + static float f1=0.123f, f2=0.0f; + ImGui::SliderFloat("slider float", &f1, 0.0f, 1.0f, "ratio = %.3f"); + ImGui::SliderFloat("slider float (curve)", &f2, -10.0f, 10.0f, "%.4f", 2.0f); + static float angle = 0.0f; + ImGui::SliderAngle("slider angle", &angle); + } - { - static int i1=0; - ImGui::SliderInt("slider int", &i1, -1, 3); - ImGui::SameLine(); ShowHelpMarker("CTRL+click to input value."); - - static float f1=0.123f, f2=0.0f; - ImGui::SliderFloat("slider float", &f1, 0.0f, 1.0f, "ratio = %.3f"); - ImGui::SliderFloat("slider float (curve)", &f2, -10.0f, 10.0f, "%.4f", 2.0f); - static float angle = 0.0f; - ImGui::SliderAngle("slider angle", &angle); - } + { + static float col1[3] = { 1.0f,0.0f,0.2f }; + static float col2[4] = { 0.4f,0.7f,0.0f,0.5f }; + ImGui::ColorEdit3("color 1", col1); + ImGui::SameLine(); ShowHelpMarker("Click on the colored square to open a color picker.\nClick and hold to use drag and drop.\nRight-click on the colored square to show options.\nCTRL+click on individual component to input value.\n"); - { - static float col1[3] = { 1.0f,0.0f,0.2f }; - static float col2[4] = { 0.4f,0.7f,0.0f,0.5f }; - ImGui::ColorEdit3("color 1", col1); - ImGui::SameLine(); ShowHelpMarker("Click on the colored square to open a color picker.\nClick and hold to use drag and drop.\nRight-click on the colored square to show options.\nCTRL+click on individual component to input value.\n"); + ImGui::ColorEdit4("color 2", col2); + } - ImGui::ColorEdit4("color 2", col2); - } + { + // List box + const char* listbox_items[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pineapple", "Strawberry", "Watermelon" }; + static int listbox_item_current = 1; + ImGui::ListBox("listbox\n(single select)", &listbox_item_current, listbox_items, IM_ARRAYSIZE(listbox_items), 4); - { - // List box - const char* listbox_items[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pineapple", "Strawberry", "Watermelon" }; - static int listbox_item_current = 1; - ImGui::ListBox("listbox\n(single select)", &listbox_item_current, listbox_items, IM_ARRAYSIZE(listbox_items), 4); - - //static int listbox_item_current2 = 2; - //ImGui::PushItemWidth(-1); - //ImGui::ListBox("##listbox2", &listbox_item_current2, listbox_items, IM_ARRAYSIZE(listbox_items), 4); - //ImGui::PopItemWidth(); - } + //static int listbox_item_current2 = 2; + //ImGui::PushItemWidth(-1); + //ImGui::ListBox("##listbox2", &listbox_item_current2, listbox_items, IM_ARRAYSIZE(listbox_items), 4); + //ImGui::PopItemWidth(); + } + + ImGui::TreePop(); + } + + // Testing ImGuiOnceUponAFrame helper. + //static ImGuiOnceUponAFrame once; + //for (int i = 0; i < 5; i++) + // if (once) + // ImGui::Text("This will be displayed only once."); + if (ImGui::TreeNode("Trees")) + { + if (ImGui::TreeNode("Basic trees")) + { + for (int i = 0; i < 5; i++) + if (ImGui::TreeNode((void*)(intptr_t)i, "Child %d", i)) + { + ImGui::Text("blah blah"); + ImGui::SameLine(); + if (ImGui::SmallButton("button")) { }; + ImGui::TreePop(); + } ImGui::TreePop(); } - // Testing ImGuiOnceUponAFrame helper. - //static ImGuiOnceUponAFrame once; - //for (int i = 0; i < 5; i++) - // if (once) - // ImGui::Text("This will be displayed only once."); - - if (ImGui::TreeNode("Trees")) + if (ImGui::TreeNode("Advanced, with Selectable nodes")) { - if (ImGui::TreeNode("Basic trees")) - { - for (int i = 0; i < 5; i++) - if (ImGui::TreeNode((void*)(intptr_t)i, "Child %d", i)) - { - ImGui::Text("blah blah"); - ImGui::SameLine(); - if (ImGui::SmallButton("button")) { }; - ImGui::TreePop(); - } - ImGui::TreePop(); - } + ShowHelpMarker("This is a more standard looking tree with selectable nodes.\nClick to select, CTRL+Click to toggle, click on arrows or double-click to open."); + static bool align_label_with_current_x_position = false; + ImGui::Checkbox("Align label with current X position)", &align_label_with_current_x_position); + ImGui::Text("Hello!"); + if (align_label_with_current_x_position) + ImGui::Unindent(ImGui::GetTreeNodeToLabelSpacing()); - if (ImGui::TreeNode("Advanced, with Selectable nodes")) + static int selection_mask = (1 << 2); // Dumb representation of what may be user-side selection state. You may carry selection state inside or outside your objects in whatever format you see fit. + int node_clicked = -1; // Temporary storage of what node we have clicked to process selection at the end of the loop. May be a pointer to your own node type, etc. + ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, ImGui::GetFontSize()*3); // Increase spacing to differentiate leaves from expanded contents. + for (int i = 0; i < 6; i++) { - ShowHelpMarker("This is a more standard looking tree with selectable nodes.\nClick to select, CTRL+Click to toggle, click on arrows or double-click to open."); - static bool align_label_with_current_x_position = false; - ImGui::Checkbox("Align label with current X position)", &align_label_with_current_x_position); - ImGui::Text("Hello!"); - if (align_label_with_current_x_position) - ImGui::Unindent(ImGui::GetTreeNodeToLabelSpacing()); - - static int selection_mask = (1 << 2); // Dumb representation of what may be user-side selection state. You may carry selection state inside or outside your objects in whatever format you see fit. - int node_clicked = -1; // Temporary storage of what node we have clicked to process selection at the end of the loop. May be a pointer to your own node type, etc. - ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, ImGui::GetFontSize()*3); // Increase spacing to differentiate leaves from expanded contents. - for (int i = 0; i < 6; i++) + // Disable the default open on single-click behavior and pass in Selected flag according to our selection state. + ImGuiTreeNodeFlags node_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ((selection_mask & (1 << i)) ? ImGuiTreeNodeFlags_Selected : 0); + if (i < 3) { - // Disable the default open on single-click behavior and pass in Selected flag according to our selection state. - ImGuiTreeNodeFlags node_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ((selection_mask & (1 << i)) ? ImGuiTreeNodeFlags_Selected : 0); - if (i < 3) + // Node + bool node_open = ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Node %d", i); + if (ImGui::IsItemClicked()) + node_clicked = i; + if (node_open) { - // Node - bool node_open = ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Node %d", i); - if (ImGui::IsItemClicked()) - node_clicked = i; - if (node_open) - { - ImGui::Text("Blah blah\nBlah Blah"); - ImGui::TreePop(); - } - } - else - { - // Leaf: The only reason we have a TreeNode at all is to allow selection of the leaf. Otherwise we can use BulletText() or TreeAdvanceToLabelPos()+Text(). - node_flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; // ImGuiTreeNodeFlags_Bullet - ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Leaf %d", i); - if (ImGui::IsItemClicked()) - node_clicked = i; + ImGui::Text("Blah blah\nBlah Blah"); + ImGui::TreePop(); } } - if (node_clicked != -1) + else { - // Update selection state. Process outside of tree loop to avoid visual inconsistencies during the clicking-frame. - if (ImGui::GetIO().KeyCtrl) - selection_mask ^= (1 << node_clicked); // CTRL+click to toggle - else //if (!(selection_mask & (1 << node_clicked))) // Depending on selection behavior you want, this commented bit preserve selection when clicking on item that is part of the selection - selection_mask = (1 << node_clicked); // Click to single-select + // Leaf: The only reason we have a TreeNode at all is to allow selection of the leaf. Otherwise we can use BulletText() or TreeAdvanceToLabelPos()+Text(). + node_flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; // ImGuiTreeNodeFlags_Bullet + ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Leaf %d", i); + if (ImGui::IsItemClicked()) + node_clicked = i; } - ImGui::PopStyleVar(); - if (align_label_with_current_x_position) - ImGui::Indent(ImGui::GetTreeNodeToLabelSpacing()); - ImGui::TreePop(); - } - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Collapsing Headers")) - { - static bool closable_group = true; - ImGui::Checkbox("Enable extra group", &closable_group); - if (ImGui::CollapsingHeader("Header")) - { - ImGui::Text("IsItemHovered: %d", IsItemHovered()); - for (int i = 0; i < 5; i++) - ImGui::Text("Some content %d", i); } - if (ImGui::CollapsingHeader("Header with a close button", &closable_group)) + if (node_clicked != -1) { - ImGui::Text("IsItemHovered: %d", IsItemHovered()); - for (int i = 0; i < 5; i++) - ImGui::Text("More content %d", i); + // Update selection state. Process outside of tree loop to avoid visual inconsistencies during the clicking-frame. + if (ImGui::GetIO().KeyCtrl) + selection_mask ^= (1 << node_clicked); // CTRL+click to toggle + else //if (!(selection_mask & (1 << node_clicked))) // Depending on selection behavior you want, this commented bit preserve selection when clicking on item that is part of the selection + selection_mask = (1 << node_clicked); // Click to single-select } + ImGui::PopStyleVar(); + if (align_label_with_current_x_position) + ImGui::Indent(ImGui::GetTreeNodeToLabelSpacing()); ImGui::TreePop(); } + ImGui::TreePop(); + } - if (ImGui::TreeNode("Bullets")) + if (ImGui::TreeNode("Collapsing Headers")) + { + static bool closable_group = true; + ImGui::Checkbox("Enable extra group", &closable_group); + if (ImGui::CollapsingHeader("Header")) { - ImGui::BulletText("Bullet point 1"); - ImGui::BulletText("Bullet point 2\nOn multiple lines"); - ImGui::Bullet(); ImGui::Text("Bullet point 3 (two calls)"); - ImGui::Bullet(); ImGui::SmallButton("Button"); - ImGui::TreePop(); + ImGui::Text("IsItemHovered: %d", ImGui::IsItemHovered()); + for (int i = 0; i < 5; i++) + ImGui::Text("Some content %d", i); } - - if (ImGui::TreeNode("Text")) + if (ImGui::CollapsingHeader("Header with a close button", &closable_group)) { - if (ImGui::TreeNode("Colored Text")) - { - // Using shortcut. You can use PushStyleColor()/PopStyleColor() for more flexibility. - ImGui::TextColored(ImVec4(1.0f,0.0f,1.0f,1.0f), "Pink"); - ImGui::TextColored(ImVec4(1.0f,1.0f,0.0f,1.0f), "Yellow"); - ImGui::TextDisabled("Disabled"); - ImGui::SameLine(); ShowHelpMarker("The TextDisabled color is stored in ImGuiStyle."); - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Word Wrapping")) - { - // Using shortcut. You can use PushTextWrapPos()/PopTextWrapPos() for more flexibility. - ImGui::TextWrapped("This text should automatically wrap on the edge of the window. The current implementation for text wrapping follows simple rules suitable for English and possibly other languages."); - ImGui::Spacing(); - - static float wrap_width = 200.0f; - ImGui::SliderFloat("Wrap width", &wrap_width, -20, 600, "%.0f"); - - ImGui::Text("Test paragraph 1:"); - ImVec2 pos = ImGui::GetCursorScreenPos(); - ImGui::GetWindowDrawList()->AddRectFilled(ImVec2(pos.x + wrap_width, pos.y), ImVec2(pos.x + wrap_width + 10, pos.y + ImGui::GetTextLineHeight()), IM_COL32(255,0,255,255)); - ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + wrap_width); - ImGui::Text("The lazy dog is a good dog. This paragraph is made to fit within %.0f pixels. Testing a 1 character word. The quick brown fox jumps over the lazy dog.", wrap_width); - ImGui::GetWindowDrawList()->AddRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), IM_COL32(255,255,0,255)); - ImGui::PopTextWrapPos(); - - ImGui::Text("Test paragraph 2:"); - pos = ImGui::GetCursorScreenPos(); - ImGui::GetWindowDrawList()->AddRectFilled(ImVec2(pos.x + wrap_width, pos.y), ImVec2(pos.x + wrap_width + 10, pos.y + ImGui::GetTextLineHeight()), IM_COL32(255,0,255,255)); - ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + wrap_width); - ImGui::Text("aaaaaaaa bbbbbbbb, c cccccccc,dddddddd. d eeeeeeee ffffffff. gggggggg!hhhhhhhh"); - ImGui::GetWindowDrawList()->AddRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), IM_COL32(255,255,0,255)); - ImGui::PopTextWrapPos(); + ImGui::Text("IsItemHovered: %d", ImGui::IsItemHovered()); + for (int i = 0; i < 5; i++) + ImGui::Text("More content %d", i); + } + ImGui::TreePop(); + } - ImGui::TreePop(); - } + if (ImGui::TreeNode("Bullets")) + { + ImGui::BulletText("Bullet point 1"); + ImGui::BulletText("Bullet point 2\nOn multiple lines"); + ImGui::Bullet(); ImGui::Text("Bullet point 3 (two calls)"); + ImGui::Bullet(); ImGui::SmallButton("Button"); + ImGui::TreePop(); + } - if (ImGui::TreeNode("UTF-8 Text")) - { - // UTF-8 test with Japanese characters - // (Needs a suitable font, try Noto, or Arial Unicode, or M+ fonts. Read misc/fonts/README.txt for details.) - // - From C++11 you can use the u8"my text" syntax to encode literal strings as UTF-8 - // - For earlier compiler, you may be able to encode your sources as UTF-8 (e.g. Visual Studio save your file as 'UTF-8 without signature') - // - FOR THIS DEMO FILE ONLY, BECAUSE WE WANT TO SUPPORT OLD COMPILERS, WE ARE *NOT* INCLUDING RAW UTF-8 CHARACTERS IN THIS SOURCE FILE. - // Instead we are encoding a few strings with hexadecimal constants. Don't do this in your application! - // Please use u8"text in any language" in your application! - // Note that characters values are preserved even by InputText() if the font cannot be displayed, so you can safely copy & paste garbled characters into another application. - ImGui::TextWrapped("CJK text will only appears if the font was loaded with the appropriate CJK character ranges. Call io.Font->LoadFromFileTTF() manually to load extra character ranges. Read misc/fonts/README.txt for details."); - ImGui::Text("Hiragana: \xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91\xe3\x81\x93 (kakikukeko)"); // Normally we would use u8"blah blah" with the proper characters directly in the string. - ImGui::Text("Kanjis: \xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e (nihongo)"); - static char buf[32] = "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e"; - //static char buf[32] = u8"NIHONGO"; // <- this is how you would write it with C++11, using real kanjis - ImGui::InputText("UTF-8 input", buf, IM_ARRAYSIZE(buf)); - ImGui::TreePop(); - } + if (ImGui::TreeNode("Text")) + { + if (ImGui::TreeNode("Colored Text")) + { + // Using shortcut. You can use PushStyleColor()/PopStyleColor() for more flexibility. + ImGui::TextColored(ImVec4(1.0f,0.0f,1.0f,1.0f), "Pink"); + ImGui::TextColored(ImVec4(1.0f,1.0f,0.0f,1.0f), "Yellow"); + ImGui::TextDisabled("Disabled"); + ImGui::SameLine(); ShowHelpMarker("The TextDisabled color is stored in ImGuiStyle."); ImGui::TreePop(); } - if (ImGui::TreeNode("Images")) + if (ImGui::TreeNode("Word Wrapping")) { - ImGuiIO& io = ImGui::GetIO(); - ImGui::TextWrapped("Below we are displaying the font texture (which is the only texture we have access to in this demo). Use the 'ImTextureID' type as storage to pass pointers or identifier to your own texture data. Hover the texture for a zoomed view!"); + // Using shortcut. You can use PushTextWrapPos()/PopTextWrapPos() for more flexibility. + ImGui::TextWrapped("This text should automatically wrap on the edge of the window. The current implementation for text wrapping follows simple rules suitable for English and possibly other languages."); + ImGui::Spacing(); - // Here we are grabbing the font texture because that's the only one we have access to inside the demo code. - // Remember that ImTextureID is just storage for whatever you want it to be, it is essentially a value that will be passed to the render function inside the ImDrawCmd structure. - // If you use one of the default imgui_impl_XXXX.cpp renderer, they all have comments at the top of their file to specify what they expect to be stored in ImTextureID. - // (for example, the imgui_impl_dx11.cpp renderer expect a 'ID3D11ShaderResourceView*' pointer. The imgui_impl_glfw_gl3.cpp renderer expect a GLuint OpenGL texture identifier etc.) - // If you decided that ImTextureID = MyEngineTexture*, then you can pass your MyEngineTexture* pointers to ImGui::Image(), and gather width/height through your own functions, etc. - // Using ShowMetricsWindow() as a "debugger" to inspect the draw data that are being passed to your render will help you debug issues if you are confused about this. - // Consider using the lower-level ImDrawList::AddImage() API, via ImGui::GetWindowDrawList()->AddImage(). - ImTextureID my_tex_id = io.Fonts->TexID; - float my_tex_w = (float)io.Fonts->TexWidth; - float my_tex_h = (float)io.Fonts->TexHeight; + static float wrap_width = 200.0f; + ImGui::SliderFloat("Wrap width", &wrap_width, -20, 600, "%.0f"); - ImGui::Text("%.0fx%.0f", my_tex_w, my_tex_h); + ImGui::Text("Test paragraph 1:"); ImVec2 pos = ImGui::GetCursorScreenPos(); - ImGui::Image(my_tex_id, ImVec2(my_tex_w, my_tex_h), ImVec2(0,0), ImVec2(1,1), ImColor(255,255,255,255), ImColor(255,255,255,128)); - if (ImGui::IsItemHovered()) - { - ImGui::BeginTooltip(); - float region_sz = 32.0f; - float region_x = io.MousePos.x - pos.x - region_sz * 0.5f; if (region_x < 0.0f) region_x = 0.0f; else if (region_x > my_tex_w - region_sz) region_x = my_tex_w - region_sz; - float region_y = io.MousePos.y - pos.y - region_sz * 0.5f; if (region_y < 0.0f) region_y = 0.0f; else if (region_y > my_tex_h - region_sz) region_y = my_tex_h - region_sz; - float zoom = 4.0f; - ImGui::Text("Min: (%.2f, %.2f)", region_x, region_y); - ImGui::Text("Max: (%.2f, %.2f)", region_x + region_sz, region_y + region_sz); - ImVec2 uv0 = ImVec2((region_x) / my_tex_w, (region_y) / my_tex_h); - ImVec2 uv1 = ImVec2((region_x + region_sz) / my_tex_w, (region_y + region_sz) / my_tex_h); - ImGui::Image(my_tex_id, ImVec2(region_sz * zoom, region_sz * zoom), uv0, uv1, ImColor(255,255,255,255), ImColor(255,255,255,128)); - ImGui::EndTooltip(); - } - ImGui::TextWrapped("And now some textured buttons.."); - static int pressed_count = 0; - for (int i = 0; i < 8; i++) - { - ImGui::PushID(i); - int frame_padding = -1 + i; // -1 = uses default padding - if (ImGui::ImageButton(my_tex_id, ImVec2(32,32), ImVec2(0,0), ImVec2(32.0f/my_tex_w,32/my_tex_h), frame_padding, ImColor(0,0,0,255))) - pressed_count += 1; - ImGui::PopID(); - ImGui::SameLine(); - } - ImGui::NewLine(); - ImGui::Text("Pressed %d times.", pressed_count); + ImGui::GetWindowDrawList()->AddRectFilled(ImVec2(pos.x + wrap_width, pos.y), ImVec2(pos.x + wrap_width + 10, pos.y + ImGui::GetTextLineHeight()), IM_COL32(255,0,255,255)); + ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + wrap_width); + ImGui::Text("The lazy dog is a good dog. This paragraph is made to fit within %.0f pixels. Testing a 1 character word. The quick brown fox jumps over the lazy dog.", wrap_width); + ImGui::GetWindowDrawList()->AddRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), IM_COL32(255,255,0,255)); + ImGui::PopTextWrapPos(); + + ImGui::Text("Test paragraph 2:"); + pos = ImGui::GetCursorScreenPos(); + ImGui::GetWindowDrawList()->AddRectFilled(ImVec2(pos.x + wrap_width, pos.y), ImVec2(pos.x + wrap_width + 10, pos.y + ImGui::GetTextLineHeight()), IM_COL32(255,0,255,255)); + ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + wrap_width); + ImGui::Text("aaaaaaaa bbbbbbbb, c cccccccc,dddddddd. d eeeeeeee ffffffff. gggggggg!hhhhhhhh"); + ImGui::GetWindowDrawList()->AddRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), IM_COL32(255,255,0,255)); + ImGui::PopTextWrapPos(); + + ImGui::TreePop(); + } + + if (ImGui::TreeNode("UTF-8 Text")) + { + // UTF-8 test with Japanese characters + // (Needs a suitable font, try Noto, or Arial Unicode, or M+ fonts. Read misc/fonts/README.txt for details.) + // - From C++11 you can use the u8"my text" syntax to encode literal strings as UTF-8 + // - For earlier compiler, you may be able to encode your sources as UTF-8 (e.g. Visual Studio save your file as 'UTF-8 without signature') + // - FOR THIS DEMO FILE ONLY, BECAUSE WE WANT TO SUPPORT OLD COMPILERS, WE ARE *NOT* INCLUDING RAW UTF-8 CHARACTERS IN THIS SOURCE FILE. + // Instead we are encoding a few strings with hexadecimal constants. Don't do this in your application! + // Please use u8"text in any language" in your application! + // Note that characters values are preserved even by InputText() if the font cannot be displayed, so you can safely copy & paste garbled characters into another application. + ImGui::TextWrapped("CJK text will only appears if the font was loaded with the appropriate CJK character ranges. Call io.Font->AddFontFromFileTTF() manually to load extra character ranges. Read misc/fonts/README.txt for details."); + ImGui::Text("Hiragana: \xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91\xe3\x81\x93 (kakikukeko)"); // Normally we would use u8"blah blah" with the proper characters directly in the string. + ImGui::Text("Kanjis: \xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e (nihongo)"); + static char buf[32] = "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e"; + //static char buf[32] = u8"NIHONGO"; // <- this is how you would write it with C++11, using real kanjis + ImGui::InputText("UTF-8 input", buf, IM_ARRAYSIZE(buf)); ImGui::TreePop(); } + ImGui::TreePop(); + } - if (ImGui::TreeNode("Combo")) + if (ImGui::TreeNode("Images")) + { + ImGuiIO& io = ImGui::GetIO(); + ImGui::TextWrapped("Below we are displaying the font texture (which is the only texture we have access to in this demo). Use the 'ImTextureID' type as storage to pass pointers or identifier to your own texture data. Hover the texture for a zoomed view!"); + + // Here we are grabbing the font texture because that's the only one we have access to inside the demo code. + // Remember that ImTextureID is just storage for whatever you want it to be, it is essentially a value that will be passed to the render function inside the ImDrawCmd structure. + // If you use one of the default imgui_impl_XXXX.cpp renderer, they all have comments at the top of their file to specify what they expect to be stored in ImTextureID. + // (for example, the imgui_impl_dx11.cpp renderer expect a 'ID3D11ShaderResourceView*' pointer. The imgui_impl_glfw_gl3.cpp renderer expect a GLuint OpenGL texture identifier etc.) + // If you decided that ImTextureID = MyEngineTexture*, then you can pass your MyEngineTexture* pointers to ImGui::Image(), and gather width/height through your own functions, etc. + // Using ShowMetricsWindow() as a "debugger" to inspect the draw data that are being passed to your render will help you debug issues if you are confused about this. + // Consider using the lower-level ImDrawList::AddImage() API, via ImGui::GetWindowDrawList()->AddImage(). + ImTextureID my_tex_id = io.Fonts->TexID; + float my_tex_w = (float)io.Fonts->TexWidth; + float my_tex_h = (float)io.Fonts->TexHeight; + + ImGui::Text("%.0fx%.0f", my_tex_w, my_tex_h); + ImVec2 pos = ImGui::GetCursorScreenPos(); + ImGui::Image(my_tex_id, ImVec2(my_tex_w, my_tex_h), ImVec2(0,0), ImVec2(1,1), ImColor(255,255,255,255), ImColor(255,255,255,128)); + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + float region_sz = 32.0f; + float region_x = io.MousePos.x - pos.x - region_sz * 0.5f; if (region_x < 0.0f) region_x = 0.0f; else if (region_x > my_tex_w - region_sz) region_x = my_tex_w - region_sz; + float region_y = io.MousePos.y - pos.y - region_sz * 0.5f; if (region_y < 0.0f) region_y = 0.0f; else if (region_y > my_tex_h - region_sz) region_y = my_tex_h - region_sz; + float zoom = 4.0f; + ImGui::Text("Min: (%.2f, %.2f)", region_x, region_y); + ImGui::Text("Max: (%.2f, %.2f)", region_x + region_sz, region_y + region_sz); + ImVec2 uv0 = ImVec2((region_x) / my_tex_w, (region_y) / my_tex_h); + ImVec2 uv1 = ImVec2((region_x + region_sz) / my_tex_w, (region_y + region_sz) / my_tex_h); + ImGui::Image(my_tex_id, ImVec2(region_sz * zoom, region_sz * zoom), uv0, uv1, ImColor(255,255,255,255), ImColor(255,255,255,128)); + ImGui::EndTooltip(); + } + ImGui::TextWrapped("And now some textured buttons.."); + static int pressed_count = 0; + for (int i = 0; i < 8; i++) { - // Expose flags as checkbox for the demo - static ImGuiComboFlags flags = 0; - ImGui::CheckboxFlags("ImGuiComboFlags_PopupAlignLeft", (unsigned int*)&flags, ImGuiComboFlags_PopupAlignLeft); - if (ImGui::CheckboxFlags("ImGuiComboFlags_NoArrowButton", (unsigned int*)&flags, ImGuiComboFlags_NoArrowButton)) - flags &= ~ImGuiComboFlags_NoPreview; // Clear the other flag, as we cannot combine both - if (ImGui::CheckboxFlags("ImGuiComboFlags_NoPreview", (unsigned int*)&flags, ImGuiComboFlags_NoPreview)) - flags &= ~ImGuiComboFlags_NoArrowButton; // Clear the other flag, as we cannot combine both + ImGui::PushID(i); + int frame_padding = -1 + i; // -1 = uses default padding + if (ImGui::ImageButton(my_tex_id, ImVec2(32,32), ImVec2(0,0), ImVec2(32.0f/my_tex_w,32/my_tex_h), frame_padding, ImColor(0,0,0,255))) + pressed_count += 1; + ImGui::PopID(); + ImGui::SameLine(); + } + ImGui::NewLine(); + ImGui::Text("Pressed %d times.", pressed_count); + ImGui::TreePop(); + } - // General BeginCombo() API, you have full control over your selection data and display type. - // (your selection data could be an index, a pointer to the object, an id for the object, a flag stored in the object itself, etc.) - const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLLLLL", "MMMM", "OOOOOOO" }; - static const char* item_current = items[0]; // Here our selection is a single pointer stored outside the object. - if (ImGui::BeginCombo("combo 1", item_current, flags)) // The second parameter is the label previewed before opening the combo. + if (ImGui::TreeNode("Combo")) + { + // Expose flags as checkbox for the demo + static ImGuiComboFlags flags = 0; + ImGui::CheckboxFlags("ImGuiComboFlags_PopupAlignLeft", (unsigned int*)&flags, ImGuiComboFlags_PopupAlignLeft); + if (ImGui::CheckboxFlags("ImGuiComboFlags_NoArrowButton", (unsigned int*)&flags, ImGuiComboFlags_NoArrowButton)) + flags &= ~ImGuiComboFlags_NoPreview; // Clear the other flag, as we cannot combine both + if (ImGui::CheckboxFlags("ImGuiComboFlags_NoPreview", (unsigned int*)&flags, ImGuiComboFlags_NoPreview)) + flags &= ~ImGuiComboFlags_NoArrowButton; // Clear the other flag, as we cannot combine both + + // General BeginCombo() API, you have full control over your selection data and display type. + // (your selection data could be an index, a pointer to the object, an id for the object, a flag stored in the object itself, etc.) + const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLLLLL", "MMMM", "OOOOOOO" }; + static const char* item_current = items[0]; // Here our selection is a single pointer stored outside the object. + if (ImGui::BeginCombo("combo 1", item_current, flags)) // The second parameter is the label previewed before opening the combo. + { + for (int n = 0; n < IM_ARRAYSIZE(items); n++) { - for (int n = 0; n < IM_ARRAYSIZE(items); n++) - { - bool is_selected = (item_current == items[n]); - if (ImGui::Selectable(items[n], is_selected)) - item_current = items[n]; - if (is_selected) - ImGui::SetItemDefaultFocus(); // Set the initial focus when opening the combo (scrolling + for keyboard navigation support in the upcoming navigation branch) - } - ImGui::EndCombo(); + bool is_selected = (item_current == items[n]); + if (ImGui::Selectable(items[n], is_selected)) + item_current = items[n]; + if (is_selected) + ImGui::SetItemDefaultFocus(); // Set the initial focus when opening the combo (scrolling + for keyboard navigation support in the upcoming navigation branch) } + ImGui::EndCombo(); + } + + // Simplified one-liner Combo() API, using values packed in a single constant string + static int item_current_2 = 0; + ImGui::Combo("combo 2 (one-liner)", &item_current_2, "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0"); - // Simplified one-liner Combo() API, using values packed in a single constant string - static int item_current_2 = 0; - ImGui::Combo("combo 2 (one-liner)", &item_current_2, "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0"); + // Simplified one-liner Combo() using an array of const char* + static int item_current_3 = -1; // If the selection isn't within 0..count, Combo won't display a preview + ImGui::Combo("combo 3 (array)", &item_current_3, items, IM_ARRAYSIZE(items)); - // Simplified one-liner Combo() using an array of const char* - static int item_current_3 = -1; // If the selection isn't within 0..count, Combo won't display a preview - ImGui::Combo("combo 3 (array)", &item_current_3, items, IM_ARRAYSIZE(items)); + // Simplified one-liner Combo() using an accessor function + struct FuncHolder { static bool ItemGetter(void* data, int idx, const char** out_str) { *out_str = ((const char**)data)[idx]; return true; } }; + static int item_current_4 = 0; + ImGui::Combo("combo 4 (function)", &item_current_4, &FuncHolder::ItemGetter, items, IM_ARRAYSIZE(items)); - // Simplified one-liner Combo() using an accessor function - struct FuncHolder { static bool ItemGetter(void* data, int idx, const char** out_str) { *out_str = ((const char**)data)[idx]; return true; } }; - static int item_current_4 = 0; - ImGui::Combo("combo 4 (function)", &item_current_4, &FuncHolder::ItemGetter, items, IM_ARRAYSIZE(items)); + ImGui::TreePop(); + } + if (ImGui::TreeNode("Selectables")) + { + // Selectable() has 2 overloads: + // - The one taking "bool selected" as a read-only selection information. When Selectable() has been clicked is returns true and you can alter selection state accordingly. + // - The one taking "bool* p_selected" as a read-write selection information (convenient in some cases) + // The earlier is more flexible, as in real application your selection may be stored in a different manner (in flags within objects, as an external list, etc). + if (ImGui::TreeNode("Basic")) + { + static bool selection[5] = { false, true, false, false, false }; + ImGui::Selectable("1. I am selectable", &selection[0]); + ImGui::Selectable("2. I am selectable", &selection[1]); + ImGui::Text("3. I am not selectable"); + ImGui::Selectable("4. I am selectable", &selection[3]); + if (ImGui::Selectable("5. I am double clickable", selection[4], ImGuiSelectableFlags_AllowDoubleClick)) + if (ImGui::IsMouseDoubleClicked(0)) + selection[4] = !selection[4]; ImGui::TreePop(); } - - if (ImGui::TreeNode("Selectables")) + if (ImGui::TreeNode("Selection State: Single Selection")) { - // Selectable() has 2 overloads: - // - The one taking "bool selected" as a read-only selection information. When Selectable() has been clicked is returns true and you can alter selection state accordingly. - // - The one taking "bool* p_selected" as a read-write selection information (convenient in some cases) - // The earlier is more flexible, as in real application your selection may be stored in a different manner (in flags within objects, as an external list, etc). - if (ImGui::TreeNode("Basic")) - { - static bool selection[5] = { false, true, false, false, false }; - ImGui::Selectable("1. I am selectable", &selection[0]); - ImGui::Selectable("2. I am selectable", &selection[1]); - ImGui::Text("3. I am not selectable"); - ImGui::Selectable("4. I am selectable", &selection[3]); - if (ImGui::Selectable("5. I am double clickable", selection[4], ImGuiSelectableFlags_AllowDoubleClick)) - if (ImGui::IsMouseDoubleClicked(0)) - selection[4] = !selection[4]; - ImGui::TreePop(); - } - if (ImGui::TreeNode("Selection State: Single Selection")) + static int selected = -1; + for (int n = 0; n < 5; n++) { - static int selected = -1; - for (int n = 0; n < 5; n++) - { - char buf[32]; - sprintf(buf, "Object %d", n); - if (ImGui::Selectable(buf, selected == n)) - selected = n; - } - ImGui::TreePop(); + char buf[32]; + sprintf(buf, "Object %d", n); + if (ImGui::Selectable(buf, selected == n)) + selected = n; } - if (ImGui::TreeNode("Selection State: Multiple Selection")) + ImGui::TreePop(); + } + if (ImGui::TreeNode("Selection State: Multiple Selection")) + { + ShowHelpMarker("Hold CTRL and click to select multiple items."); + static bool selection[5] = { false, false, false, false, false }; + for (int n = 0; n < 5; n++) { - ShowHelpMarker("Hold CTRL and click to select multiple items."); - static bool selection[5] = { false, false, false, false, false }; - for (int n = 0; n < 5; n++) + char buf[32]; + sprintf(buf, "Object %d", n); + if (ImGui::Selectable(buf, selection[n])) { - char buf[32]; - sprintf(buf, "Object %d", n); - if (ImGui::Selectable(buf, selection[n])) - { - if (!ImGui::GetIO().KeyCtrl) // Clear selection when CTRL is not held - memset(selection, 0, sizeof(selection)); - selection[n] ^= 1; - } + if (!ImGui::GetIO().KeyCtrl) // Clear selection when CTRL is not held + memset(selection, 0, sizeof(selection)); + selection[n] ^= 1; } - ImGui::TreePop(); - } - if (ImGui::TreeNode("Rendering more text into the same line")) - { - // Using the Selectable() override that takes "bool* p_selected" parameter and toggle your booleans automatically. - static bool selected[3] = { false, false, false }; - ImGui::Selectable("main.c", &selected[0]); ImGui::SameLine(300); ImGui::Text(" 2,345 bytes"); - ImGui::Selectable("Hello.cpp", &selected[1]); ImGui::SameLine(300); ImGui::Text("12,345 bytes"); - ImGui::Selectable("Hello.h", &selected[2]); ImGui::SameLine(300); ImGui::Text(" 2,345 bytes"); - ImGui::TreePop(); } - if (ImGui::TreeNode("In columns")) + ImGui::TreePop(); + } + if (ImGui::TreeNode("Rendering more text into the same line")) + { + // Using the Selectable() override that takes "bool* p_selected" parameter and toggle your booleans automatically. + static bool selected[3] = { false, false, false }; + ImGui::Selectable("main.c", &selected[0]); ImGui::SameLine(300); ImGui::Text(" 2,345 bytes"); + ImGui::Selectable("Hello.cpp", &selected[1]); ImGui::SameLine(300); ImGui::Text("12,345 bytes"); + ImGui::Selectable("Hello.h", &selected[2]); ImGui::SameLine(300); ImGui::Text(" 2,345 bytes"); + ImGui::TreePop(); + } + if (ImGui::TreeNode("In columns")) + { + ImGui::Columns(3, NULL, false); + static bool selected[16] = { 0 }; + for (int i = 0; i < 16; i++) { - ImGui::Columns(3, NULL, false); - static bool selected[16] = { 0 }; - for (int i = 0; i < 16; i++) - { - char label[32]; sprintf(label, "Item %d", i); - if (ImGui::Selectable(label, &selected[i])) {} - ImGui::NextColumn(); - } - ImGui::Columns(1); - ImGui::TreePop(); + char label[32]; sprintf(label, "Item %d", i); + if (ImGui::Selectable(label, &selected[i])) {} + ImGui::NextColumn(); } - if (ImGui::TreeNode("Grid")) + ImGui::Columns(1); + ImGui::TreePop(); + } + if (ImGui::TreeNode("Grid")) + { + static bool selected[16] = { true, false, false, false, false, true, false, false, false, false, true, false, false, false, false, true }; + for (int i = 0; i < 16; i++) { - static bool selected[16] = { true, false, false, false, false, true, false, false, false, false, true, false, false, false, false, true }; - for (int i = 0; i < 16; i++) + ImGui::PushID(i); + if (ImGui::Selectable("Sailor", &selected[i], 0, ImVec2(50,50))) { - ImGui::PushID(i); - if (ImGui::Selectable("Sailor", &selected[i], 0, ImVec2(50,50))) - { - int x = i % 4, y = i / 4; - if (x > 0) selected[i - 1] ^= 1; - if (x < 3) selected[i + 1] ^= 1; - if (y > 0) selected[i - 4] ^= 1; - if (y < 3) selected[i + 4] ^= 1; - } - if ((i % 4) < 3) ImGui::SameLine(); - ImGui::PopID(); + int x = i % 4, y = i / 4; + if (x > 0) selected[i - 1] ^= 1; + if (x < 3) selected[i + 1] ^= 1; + if (y > 0) selected[i - 4] ^= 1; + if (y < 3) selected[i + 4] ^= 1; } - ImGui::TreePop(); + if ((i % 4) < 3) ImGui::SameLine(); + ImGui::PopID(); } ImGui::TreePop(); } + ImGui::TreePop(); + } - if (ImGui::TreeNode("Filtered Text Input")) - { - static char buf1[64] = ""; ImGui::InputText("default", buf1, 64); - static char buf2[64] = ""; ImGui::InputText("decimal", buf2, 64, ImGuiInputTextFlags_CharsDecimal); - static char buf3[64] = ""; ImGui::InputText("hexadecimal", buf3, 64, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase); - static char buf4[64] = ""; ImGui::InputText("uppercase", buf4, 64, ImGuiInputTextFlags_CharsUppercase); - static char buf5[64] = ""; ImGui::InputText("no blank", buf5, 64, ImGuiInputTextFlags_CharsNoBlank); - struct TextFilters { static int FilterImGuiLetters(ImGuiInputTextCallbackData* data) { if (data->EventChar < 256 && strchr("imgui", (char)data->EventChar)) return 0; return 1; } }; - static char buf6[64] = ""; ImGui::InputText("\"imgui\" letters", buf6, 64, ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterImGuiLetters); + if (ImGui::TreeNode("Filtered Text Input")) + { + static char buf1[64] = ""; ImGui::InputText("default", buf1, 64); + static char buf2[64] = ""; ImGui::InputText("decimal", buf2, 64, ImGuiInputTextFlags_CharsDecimal); + static char buf3[64] = ""; ImGui::InputText("hexadecimal", buf3, 64, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase); + static char buf4[64] = ""; ImGui::InputText("uppercase", buf4, 64, ImGuiInputTextFlags_CharsUppercase); + static char buf5[64] = ""; ImGui::InputText("no blank", buf5, 64, ImGuiInputTextFlags_CharsNoBlank); + struct TextFilters { static int FilterImGuiLetters(ImGuiInputTextCallbackData* data) { if (data->EventChar < 256 && strchr("imgui", (char)data->EventChar)) return 0; return 1; } }; + static char buf6[64] = ""; ImGui::InputText("\"imgui\" letters", buf6, 64, ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterImGuiLetters); + + ImGui::Text("Password input"); + static char bufpass[64] = "password123"; + ImGui::InputText("password", bufpass, 64, ImGuiInputTextFlags_Password | ImGuiInputTextFlags_CharsNoBlank); + ImGui::SameLine(); ShowHelpMarker("Display all characters as '*'.\nDisable clipboard cut and copy.\nDisable logging.\n"); + ImGui::InputText("password (clear)", bufpass, 64, ImGuiInputTextFlags_CharsNoBlank); - ImGui::Text("Password input"); - static char bufpass[64] = "password123"; - ImGui::InputText("password", bufpass, 64, ImGuiInputTextFlags_Password | ImGuiInputTextFlags_CharsNoBlank); - ImGui::SameLine(); ShowHelpMarker("Display all characters as '*'.\nDisable clipboard cut and copy.\nDisable logging.\n"); - ImGui::InputText("password (clear)", bufpass, 64, ImGuiInputTextFlags_CharsNoBlank); + ImGui::TreePop(); + } - ImGui::TreePop(); - } + if (ImGui::TreeNode("Multi-line Text Input")) + { + static bool read_only = false; + static char text[1024*16] = + "/*\n" + " The Pentium F00F bug, shorthand for F0 0F C7 C8,\n" + " the hexadecimal encoding of one offending instruction,\n" + " more formally, the invalid operand with locked CMPXCHG8B\n" + " instruction bug, is a design flaw in the majority of\n" + " Intel Pentium, Pentium MMX, and Pentium OverDrive\n" + " processors (all in the P5 microarchitecture).\n" + "*/\n\n" + "label:\n" + "\tlock cmpxchg8b eax\n"; + + ShowHelpMarker("You can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputTextMultiline() to a dynamic string type. See misc/cpp/imgui_stdlib.h for an example. (This is not demonstrated in imgui_demo.cpp)"); + ImGui::Checkbox("Read-only", &read_only); + ImGuiInputTextFlags flags = ImGuiInputTextFlags_AllowTabInput | (read_only ? ImGuiInputTextFlags_ReadOnly : 0); + ImGui::InputTextMultiline("##source", text, IM_ARRAYSIZE(text), ImVec2(-1.0f, ImGui::GetTextLineHeight() * 16), flags); + ImGui::TreePop(); + } - if (ImGui::TreeNode("Multi-line Text Input")) - { - static bool read_only = false; - static char text[1024*16] = - "/*\n" - " The Pentium F00F bug, shorthand for F0 0F C7 C8,\n" - " the hexadecimal encoding of one offending instruction,\n" - " more formally, the invalid operand with locked CMPXCHG8B\n" - " instruction bug, is a design flaw in the majority of\n" - " Intel Pentium, Pentium MMX, and Pentium OverDrive\n" - " processors (all in the P5 microarchitecture).\n" - "*/\n\n" - "label:\n" - "\tlock cmpxchg8b eax\n"; - - ShowHelpMarker("You can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputTextMultiline() to a dynamic string type. See misc/stl/imgui_stl.h for an example. (This is not demonstrated in imgui_demo.cpp)"); - ImGui::Checkbox("Read-only", &read_only); - ImGuiInputTextFlags flags = ImGuiInputTextFlags_AllowTabInput | (read_only ? ImGuiInputTextFlags_ReadOnly : 0); - ImGui::InputTextMultiline("##source", text, IM_ARRAYSIZE(text), ImVec2(-1.0f, ImGui::GetTextLineHeight() * 16), flags); - ImGui::TreePop(); - } + if (ImGui::TreeNode("Plots Widgets")) + { + static bool animate = true; + ImGui::Checkbox("Animate", &animate); + + static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f }; + ImGui::PlotLines("Frame Times", arr, IM_ARRAYSIZE(arr)); + + // Create a dummy array of contiguous float values to plot + // Tip: If your float aren't contiguous but part of a structure, you can pass a pointer to your first float and the sizeof() of your structure in the Stride parameter. + static float values[90] = { 0 }; + static int values_offset = 0; + static double refresh_time = 0.0; + if (!animate || refresh_time == 0.0f) + refresh_time = ImGui::GetTime(); + while (refresh_time < ImGui::GetTime()) // Create dummy data at fixed 60 hz rate for the demo + { + static float phase = 0.0f; + values[values_offset] = cosf(phase); + values_offset = (values_offset+1) % IM_ARRAYSIZE(values); + phase += 0.10f*values_offset; + refresh_time += 1.0f/60.0f; + } + ImGui::PlotLines("Lines", values, IM_ARRAYSIZE(values), values_offset, "avg 0.0", -1.0f, 1.0f, ImVec2(0,80)); + ImGui::PlotHistogram("Histogram", arr, IM_ARRAYSIZE(arr), 0, NULL, 0.0f, 1.0f, ImVec2(0,80)); + + // Use functions to generate output + // FIXME: This is rather awkward because current plot API only pass in indices. We probably want an API passing floats and user provide sample rate/count. + struct Funcs + { + static float Sin(void*, int i) { return sinf(i * 0.1f); } + static float Saw(void*, int i) { return (i & 1) ? 1.0f : -1.0f; } + }; + static int func_type = 0, display_count = 70; + ImGui::Separator(); + ImGui::PushItemWidth(100); ImGui::Combo("func", &func_type, "Sin\0Saw\0"); ImGui::PopItemWidth(); + ImGui::SameLine(); + ImGui::SliderInt("Sample count", &display_count, 1, 400); + float (*func)(void*, int) = (func_type == 0) ? Funcs::Sin : Funcs::Saw; + ImGui::PlotLines("Lines", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0,80)); + ImGui::PlotHistogram("Histogram", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0,80)); + ImGui::Separator(); - if (ImGui::TreeNode("Plots Widgets")) + // Animate a simple progress bar + static float progress = 0.0f, progress_dir = 1.0f; + if (animate) { - static bool animate = true; - ImGui::Checkbox("Animate", &animate); - - static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f }; - ImGui::PlotLines("Frame Times", arr, IM_ARRAYSIZE(arr)); - - // Create a dummy array of contiguous float values to plot - // Tip: If your float aren't contiguous but part of a structure, you can pass a pointer to your first float and the sizeof() of your structure in the Stride parameter. - static float values[90] = { 0 }; - static int values_offset = 0; - static double refresh_time = 0.0; - if (!animate || refresh_time == 0.0f) - refresh_time = ImGui::GetTime(); - while (refresh_time < ImGui::GetTime()) // Create dummy data at fixed 60 hz rate for the demo - { - static float phase = 0.0f; - values[values_offset] = cosf(phase); - values_offset = (values_offset+1) % IM_ARRAYSIZE(values); - phase += 0.10f*values_offset; - refresh_time += 1.0f/60.0f; - } - ImGui::PlotLines("Lines", values, IM_ARRAYSIZE(values), values_offset, "avg 0.0", -1.0f, 1.0f, ImVec2(0,80)); - ImGui::PlotHistogram("Histogram", arr, IM_ARRAYSIZE(arr), 0, NULL, 0.0f, 1.0f, ImVec2(0,80)); + progress += progress_dir * 0.4f * ImGui::GetIO().DeltaTime; + if (progress >= +1.1f) { progress = +1.1f; progress_dir *= -1.0f; } + if (progress <= -0.1f) { progress = -0.1f; progress_dir *= -1.0f; } + } - // Use functions to generate output - // FIXME: This is rather awkward because current plot API only pass in indices. We probably want an API passing floats and user provide sample rate/count. - struct Funcs - { - static float Sin(void*, int i) { return sinf(i * 0.1f); } - static float Saw(void*, int i) { return (i & 1) ? 1.0f : -1.0f; } - }; - static int func_type = 0, display_count = 70; - ImGui::Separator(); - ImGui::PushItemWidth(100); ImGui::Combo("func", &func_type, "Sin\0Saw\0"); ImGui::PopItemWidth(); - ImGui::SameLine(); - ImGui::SliderInt("Sample count", &display_count, 1, 400); - float (*func)(void*, int) = (func_type == 0) ? Funcs::Sin : Funcs::Saw; - ImGui::PlotLines("Lines", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0,80)); - ImGui::PlotHistogram("Histogram", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0,80)); - ImGui::Separator(); + // Typically we would use ImVec2(-1.0f,0.0f) to use all available width, or ImVec2(width,0.0f) for a specified width. ImVec2(0.0f,0.0f) uses ItemWidth. + ImGui::ProgressBar(progress, ImVec2(0.0f,0.0f)); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::Text("Progress Bar"); - // Animate a simple progress bar - static float progress = 0.0f, progress_dir = 1.0f; - if (animate) - { - progress += progress_dir * 0.4f * ImGui::GetIO().DeltaTime; - if (progress >= +1.1f) { progress = +1.1f; progress_dir *= -1.0f; } - if (progress <= -0.1f) { progress = -0.1f; progress_dir *= -1.0f; } - } + float progress_saturated = (progress < 0.0f) ? 0.0f : (progress > 1.0f) ? 1.0f : progress; + char buf[32]; + sprintf(buf, "%d/%d", (int)(progress_saturated*1753), 1753); + ImGui::ProgressBar(progress, ImVec2(0.f,0.f), buf); + ImGui::TreePop(); + } - // Typically we would use ImVec2(-1.0f,0.0f) to use all available width, or ImVec2(width,0.0f) for a specified width. ImVec2(0.0f,0.0f) uses ItemWidth. - ImGui::ProgressBar(progress, ImVec2(0.0f,0.0f)); - ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); - ImGui::Text("Progress Bar"); + if (ImGui::TreeNode("Color/Picker Widgets")) + { + static ImVec4 color = ImColor(114, 144, 154, 200); - float progress_saturated = (progress < 0.0f) ? 0.0f : (progress > 1.0f) ? 1.0f : progress; - char buf[32]; - sprintf(buf, "%d/%d", (int)(progress_saturated*1753), 1753); - ImGui::ProgressBar(progress, ImVec2(0.f,0.f), buf); - ImGui::TreePop(); - } + static bool alpha_preview = true; + static bool alpha_half_preview = false; + static bool drag_and_drop = true; + static bool options_menu = true; + static bool hdr = false; + ImGui::Checkbox("With Alpha Preview", &alpha_preview); + ImGui::Checkbox("With Half Alpha Preview", &alpha_half_preview); + ImGui::Checkbox("With Drag and Drop", &drag_and_drop); + ImGui::Checkbox("With Options Menu", &options_menu); ImGui::SameLine(); ShowHelpMarker("Right-click on the individual color widget to show options."); + ImGui::Checkbox("With HDR", &hdr); ImGui::SameLine(); ShowHelpMarker("Currently all this does is to lift the 0..1 limits on dragging widgets."); + int misc_flags = (hdr ? ImGuiColorEditFlags_HDR : 0) | (drag_and_drop ? 0 : ImGuiColorEditFlags_NoDragDrop) | (alpha_half_preview ? ImGuiColorEditFlags_AlphaPreviewHalf : (alpha_preview ? ImGuiColorEditFlags_AlphaPreview : 0)) | (options_menu ? 0 : ImGuiColorEditFlags_NoOptions); - if (ImGui::TreeNode("Color/Picker Widgets")) - { - static ImVec4 color = ImColor(114, 144, 154, 200); + ImGui::Text("Color widget:"); + ImGui::SameLine(); ShowHelpMarker("Click on the colored square to open a color picker.\nCTRL+click on individual component to input value.\n"); + ImGui::ColorEdit3("MyColor##1", (float*)&color, misc_flags); - static bool alpha_preview = true; - static bool alpha_half_preview = false; - static bool drag_and_drop = true; - static bool options_menu = true; - static bool hdr = false; - ImGui::Checkbox("With Alpha Preview", &alpha_preview); - ImGui::Checkbox("With Half Alpha Preview", &alpha_half_preview); - ImGui::Checkbox("With Drag and Drop", &drag_and_drop); - ImGui::Checkbox("With Options Menu", &options_menu); ImGui::SameLine(); ShowHelpMarker("Right-click on the individual color widget to show options."); - ImGui::Checkbox("With HDR", &hdr); ImGui::SameLine(); ShowHelpMarker("Currently all this does is to lift the 0..1 limits on dragging widgets."); - int misc_flags = (hdr ? ImGuiColorEditFlags_HDR : 0) | (drag_and_drop ? 0 : ImGuiColorEditFlags_NoDragDrop) | (alpha_half_preview ? ImGuiColorEditFlags_AlphaPreviewHalf : (alpha_preview ? ImGuiColorEditFlags_AlphaPreview : 0)) | (options_menu ? 0 : ImGuiColorEditFlags_NoOptions); + ImGui::Text("Color widget HSV with Alpha:"); + ImGui::ColorEdit4("MyColor##2", (float*)&color, ImGuiColorEditFlags_HSV | misc_flags); - ImGui::Text("Color widget:"); - ImGui::SameLine(); ShowHelpMarker("Click on the colored square to open a color picker.\nCTRL+click on individual component to input value.\n"); - ImGui::ColorEdit3("MyColor##1", (float*)&color, misc_flags); + ImGui::Text("Color widget with Float Display:"); + ImGui::ColorEdit4("MyColor##2f", (float*)&color, ImGuiColorEditFlags_Float | misc_flags); - ImGui::Text("Color widget HSV with Alpha:"); - ImGui::ColorEdit4("MyColor##2", (float*)&color, ImGuiColorEditFlags_HSV | misc_flags); + ImGui::Text("Color button with Picker:"); + ImGui::SameLine(); ShowHelpMarker("With the ImGuiColorEditFlags_NoInputs flag you can hide all the slider/text inputs.\nWith the ImGuiColorEditFlags_NoLabel flag you can pass a non-empty label which will only be used for the tooltip and picker popup."); + ImGui::ColorEdit4("MyColor##3", (float*)&color, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel | misc_flags); - ImGui::Text("Color widget with Float Display:"); - ImGui::ColorEdit4("MyColor##2f", (float*)&color, ImGuiColorEditFlags_Float | misc_flags); + ImGui::Text("Color button with Custom Picker Popup:"); - ImGui::Text("Color button with Picker:"); - ImGui::SameLine(); ShowHelpMarker("With the ImGuiColorEditFlags_NoInputs flag you can hide all the slider/text inputs.\nWith the ImGuiColorEditFlags_NoLabel flag you can pass a non-empty label which will only be used for the tooltip and picker popup."); - ImGui::ColorEdit4("MyColor##3", (float*)&color, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel | misc_flags); + // Generate a dummy palette + static bool saved_palette_inited = false; + static ImVec4 saved_palette[32]; + if (!saved_palette_inited) + for (int n = 0; n < IM_ARRAYSIZE(saved_palette); n++) + { + ImGui::ColorConvertHSVtoRGB(n / 31.0f, 0.8f, 0.8f, saved_palette[n].x, saved_palette[n].y, saved_palette[n].z); + saved_palette[n].w = 1.0f; // Alpha + } + saved_palette_inited = true; - ImGui::Text("Color button with Custom Picker Popup:"); + static ImVec4 backup_color; + bool open_popup = ImGui::ColorButton("MyColor##3b", color, misc_flags); + ImGui::SameLine(); + open_popup |= ImGui::Button("Palette"); + if (open_popup) + { + ImGui::OpenPopup("mypicker"); + backup_color = color; + } + if (ImGui::BeginPopup("mypicker")) + { + // FIXME: Adding a drag and drop example here would be perfect! + ImGui::Text("MY CUSTOM COLOR PICKER WITH AN AMAZING PALETTE!"); + ImGui::Separator(); + ImGui::ColorPicker4("##picker", (float*)&color, misc_flags | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoSmallPreview); + ImGui::SameLine(); + ImGui::BeginGroup(); + ImGui::Text("Current"); + ImGui::ColorButton("##current", color, ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf, ImVec2(60,40)); + ImGui::Text("Previous"); + if (ImGui::ColorButton("##previous", backup_color, ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf, ImVec2(60,40))) + color = backup_color; + ImGui::Separator(); + ImGui::Text("Palette"); + for (int n = 0; n < IM_ARRAYSIZE(saved_palette); n++) + { + ImGui::PushID(n); + if ((n % 8) != 0) + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.y); + if (ImGui::ColorButton("##palette", saved_palette[n], ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_NoTooltip, ImVec2(20,20))) + color = ImVec4(saved_palette[n].x, saved_palette[n].y, saved_palette[n].z, color.w); // Preserve alpha! - // Generate a dummy palette - static bool saved_palette_inited = false; - static ImVec4 saved_palette[32]; - if (!saved_palette_inited) - for (int n = 0; n < IM_ARRAYSIZE(saved_palette); n++) + if (ImGui::BeginDragDropTarget()) { - ImGui::ColorConvertHSVtoRGB(n / 31.0f, 0.8f, 0.8f, saved_palette[n].x, saved_palette[n].y, saved_palette[n].z); - saved_palette[n].w = 1.0f; // Alpha + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F)) + memcpy((float*)&saved_palette[n], payload->Data, sizeof(float) * 3); + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F)) + memcpy((float*)&saved_palette[n], payload->Data, sizeof(float) * 4); + ImGui::EndDragDropTarget(); } - saved_palette_inited = true; - static ImVec4 backup_color; - bool open_popup = ImGui::ColorButton("MyColor##3b", color, misc_flags); - ImGui::SameLine(); - open_popup |= ImGui::Button("Palette"); - if (open_popup) - { - ImGui::OpenPopup("mypicker"); - backup_color = color; + ImGui::PopID(); } - if (ImGui::BeginPopup("mypicker")) - { - // FIXME: Adding a drag and drop example here would be perfect! - ImGui::Text("MY CUSTOM COLOR PICKER WITH AN AMAZING PALETTE!"); - ImGui::Separator(); - ImGui::ColorPicker4("##picker", (float*)&color, misc_flags | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoSmallPreview); - ImGui::SameLine(); - ImGui::BeginGroup(); - ImGui::Text("Current"); - ImGui::ColorButton("##current", color, ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf, ImVec2(60,40)); - ImGui::Text("Previous"); - if (ImGui::ColorButton("##previous", backup_color, ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf, ImVec2(60,40))) - color = backup_color; - ImGui::Separator(); - ImGui::Text("Palette"); - for (int n = 0; n < IM_ARRAYSIZE(saved_palette); n++) - { - ImGui::PushID(n); - if ((n % 8) != 0) - ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.y); - if (ImGui::ColorButton("##palette", saved_palette[n], ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_NoTooltip, ImVec2(20,20))) - color = ImVec4(saved_palette[n].x, saved_palette[n].y, saved_palette[n].z, color.w); // Preserve alpha! - - if (ImGui::BeginDragDropTarget()) - { - if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F)) - memcpy((float*)&saved_palette[n], payload->Data, sizeof(float) * 3); - if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F)) - memcpy((float*)&saved_palette[n], payload->Data, sizeof(float) * 4); - EndDragDropTarget(); - } + ImGui::EndGroup(); + ImGui::EndPopup(); + } - ImGui::PopID(); - } - ImGui::EndGroup(); - ImGui::EndPopup(); - } + ImGui::Text("Color button only:"); + ImGui::ColorButton("MyColor##3c", *(ImVec4*)&color, misc_flags, ImVec2(80,80)); - ImGui::Text("Color button only:"); - ImGui::ColorButton("MyColor##3c", *(ImVec4*)&color, misc_flags, ImVec2(80,80)); - - ImGui::Text("Color picker:"); - static bool alpha = true; - static bool alpha_bar = true; - static bool side_preview = true; - static bool ref_color = false; - static ImVec4 ref_color_v(1.0f,0.0f,1.0f,0.5f); - static int inputs_mode = 2; - static int picker_mode = 0; - ImGui::Checkbox("With Alpha", &alpha); - ImGui::Checkbox("With Alpha Bar", &alpha_bar); - ImGui::Checkbox("With Side Preview", &side_preview); - if (side_preview) + ImGui::Text("Color picker:"); + static bool alpha = true; + static bool alpha_bar = true; + static bool side_preview = true; + static bool ref_color = false; + static ImVec4 ref_color_v(1.0f,0.0f,1.0f,0.5f); + static int inputs_mode = 2; + static int picker_mode = 0; + ImGui::Checkbox("With Alpha", &alpha); + ImGui::Checkbox("With Alpha Bar", &alpha_bar); + ImGui::Checkbox("With Side Preview", &side_preview); + if (side_preview) + { + ImGui::SameLine(); + ImGui::Checkbox("With Ref Color", &ref_color); + if (ref_color) { ImGui::SameLine(); - ImGui::Checkbox("With Ref Color", &ref_color); - if (ref_color) - { - ImGui::SameLine(); - ImGui::ColorEdit4("##RefColor", &ref_color_v.x, ImGuiColorEditFlags_NoInputs | misc_flags); - } - } - ImGui::Combo("Inputs Mode", &inputs_mode, "All Inputs\0No Inputs\0RGB Input\0HSV Input\0HEX Input\0"); - ImGui::Combo("Picker Mode", &picker_mode, "Auto/Current\0Hue bar + SV rect\0Hue wheel + SV triangle\0"); - ImGui::SameLine(); ShowHelpMarker("User can right-click the picker to change mode."); - ImGuiColorEditFlags flags = misc_flags; - if (!alpha) flags |= ImGuiColorEditFlags_NoAlpha; // This is by default if you call ColorPicker3() instead of ColorPicker4() - if (alpha_bar) flags |= ImGuiColorEditFlags_AlphaBar; - if (!side_preview) flags |= ImGuiColorEditFlags_NoSidePreview; - if (picker_mode == 1) flags |= ImGuiColorEditFlags_PickerHueBar; - if (picker_mode == 2) flags |= ImGuiColorEditFlags_PickerHueWheel; - if (inputs_mode == 1) flags |= ImGuiColorEditFlags_NoInputs; - if (inputs_mode == 2) flags |= ImGuiColorEditFlags_RGB; - if (inputs_mode == 3) flags |= ImGuiColorEditFlags_HSV; - if (inputs_mode == 4) flags |= ImGuiColorEditFlags_HEX; - ImGui::ColorPicker4("MyColor##4", (float*)&color, flags, ref_color ? &ref_color_v.x : NULL); - - ImGui::Text("Programmatically set defaults:"); - ImGui::SameLine(); ShowHelpMarker("SetColorEditOptions() is designed to allow you to set boot-time default.\nWe don't have Push/Pop functions because you can force options on a per-widget basis if needed, and the user can change non-forced ones with the options menu.\nWe don't have a getter to avoid encouraging you to persistently save values that aren't forward-compatible."); - if (ImGui::Button("Default: Uint8 + HSV + Hue Bar")) - ImGui::SetColorEditOptions(ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_HSV | ImGuiColorEditFlags_PickerHueBar); - if (ImGui::Button("Default: Float + HDR + Hue Wheel")) - ImGui::SetColorEditOptions(ImGuiColorEditFlags_Float | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_PickerHueWheel); + ImGui::ColorEdit4("##RefColor", &ref_color_v.x, ImGuiColorEditFlags_NoInputs | misc_flags); + } + } + ImGui::Combo("Inputs Mode", &inputs_mode, "All Inputs\0No Inputs\0RGB Input\0HSV Input\0HEX Input\0"); + ImGui::Combo("Picker Mode", &picker_mode, "Auto/Current\0Hue bar + SV rect\0Hue wheel + SV triangle\0"); + ImGui::SameLine(); ShowHelpMarker("User can right-click the picker to change mode."); + ImGuiColorEditFlags flags = misc_flags; + if (!alpha) flags |= ImGuiColorEditFlags_NoAlpha; // This is by default if you call ColorPicker3() instead of ColorPicker4() + if (alpha_bar) flags |= ImGuiColorEditFlags_AlphaBar; + if (!side_preview) flags |= ImGuiColorEditFlags_NoSidePreview; + if (picker_mode == 1) flags |= ImGuiColorEditFlags_PickerHueBar; + if (picker_mode == 2) flags |= ImGuiColorEditFlags_PickerHueWheel; + if (inputs_mode == 1) flags |= ImGuiColorEditFlags_NoInputs; + if (inputs_mode == 2) flags |= ImGuiColorEditFlags_RGB; + if (inputs_mode == 3) flags |= ImGuiColorEditFlags_HSV; + if (inputs_mode == 4) flags |= ImGuiColorEditFlags_HEX; + ImGui::ColorPicker4("MyColor##4", (float*)&color, flags, ref_color ? &ref_color_v.x : NULL); + + ImGui::Text("Programmatically set defaults:"); + ImGui::SameLine(); ShowHelpMarker("SetColorEditOptions() is designed to allow you to set boot-time default.\nWe don't have Push/Pop functions because you can force options on a per-widget basis if needed, and the user can change non-forced ones with the options menu.\nWe don't have a getter to avoid encouraging you to persistently save values that aren't forward-compatible."); + if (ImGui::Button("Default: Uint8 + HSV + Hue Bar")) + ImGui::SetColorEditOptions(ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_HSV | ImGuiColorEditFlags_PickerHueBar); + if (ImGui::Button("Default: Float + HDR + Hue Wheel")) + ImGui::SetColorEditOptions(ImGuiColorEditFlags_Float | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_PickerHueWheel); - ImGui::TreePop(); - } + ImGui::TreePop(); + } - if (ImGui::TreeNode("Range Widgets")) - { - static float begin = 10, end = 90; - static int begin_i = 100, end_i = 1000; - ImGui::DragFloatRange2("range", &begin, &end, 0.25f, 0.0f, 100.0f, "Min: %.1f %%", "Max: %.1f %%"); - ImGui::DragIntRange2("range int (no bounds)", &begin_i, &end_i, 5, 0, 0, "Min: %d units", "Max: %d units"); - ImGui::TreePop(); - } + if (ImGui::TreeNode("Range Widgets")) + { + static float begin = 10, end = 90; + static int begin_i = 100, end_i = 1000; + ImGui::DragFloatRange2("range", &begin, &end, 0.25f, 0.0f, 100.0f, "Min: %.1f %%", "Max: %.1f %%"); + ImGui::DragIntRange2("range int (no bounds)", &begin_i, &end_i, 5, 0, 0, "Min: %d units", "Max: %d units"); + ImGui::TreePop(); + } - if (ImGui::TreeNode("Data Types")) - { - // The DragScalar/InputScalar/SliderScalar functions allow various data types: signed/unsigned int/long long and float/double - // To avoid polluting the public API with all possible combinations, we use the ImGuiDataType enum to pass the type, - // and passing all arguments by address. - // This is the reason the test code below creates local variables to hold "zero" "one" etc. for each types. - // In practice, if you frequently use a given type that is not covered by the normal API entry points, you can wrap it - // yourself inside a 1 line function which can take typed argument as value instead of void*, and then pass their address - // to the generic function. For example: - // bool MySliderU64(const char *label, u64* value, u64 min = 0, u64 max = 0, const char* format = "%lld") - // { - // return SliderScalar(label, ImGuiDataType_U64, value, &min, &max, format); - // } - - // Limits (as helper variables that we can take the address of) - // Note that the SliderScalar function has a maximum usable range of half the natural type maximum, hence the /2 below. - #ifndef LLONG_MIN - ImS64 LLONG_MIN = -9223372036854775807LL - 1; - ImS64 LLONG_MAX = 9223372036854775807LL; - ImU64 ULLONG_MAX = (2ULL * 9223372036854775807LL + 1); - #endif - const ImS32 s32_zero = 0, s32_one = 1, s32_fifty = 50, s32_min = INT_MIN/2, s32_max = INT_MAX/2, s32_hi_a = INT_MAX/2 - 100, s32_hi_b = INT_MAX/2; - const ImU32 u32_zero = 0, u32_one = 1, u32_fifty = 50, u32_min = 0, u32_max = UINT_MAX/2, u32_hi_a = UINT_MAX/2 - 100, u32_hi_b = UINT_MAX/2; - const ImS64 s64_zero = 0, s64_one = 1, s64_fifty = 50, s64_min = LLONG_MIN/2, s64_max = LLONG_MAX/2, s64_hi_a = LLONG_MAX/2 - 100, s64_hi_b = LLONG_MAX/2; - const ImU64 u64_zero = 0, u64_one = 1, u64_fifty = 50, u64_min = 0, u64_max = ULLONG_MAX/2, u64_hi_a = ULLONG_MAX/2 - 100, u64_hi_b = ULLONG_MAX/2; - const float f32_zero = 0.f, f32_one = 1.f, f32_lo_a = -10000000000.0f, f32_hi_a = +10000000000.0f; - const double f64_zero = 0., f64_one = 1., f64_lo_a = -1000000000000000.0, f64_hi_a = +1000000000000000.0; - - // State - static ImS32 s32_v = -1; - static ImU32 u32_v = (ImU32)-1; - static ImS64 s64_v = -1; - static ImU64 u64_v = (ImU64)-1; - static float f32_v = 0.123f; - static double f64_v = 90000.01234567890123456789; - - const float drag_speed = 0.2f; - static bool drag_clamp = false; - ImGui::Text("Drags:"); - ImGui::Checkbox("Clamp integers to 0..50", &drag_clamp); ImGui::SameLine(); ShowHelpMarker("As with every widgets in dear imgui, we never modify values unless there is a user interaction.\nYou can override the clamping limits by using CTRL+Click to input a value."); - ImGui::DragScalar("drag s32", ImGuiDataType_S32, &s32_v, drag_speed, drag_clamp ? &s32_zero : NULL, drag_clamp ? &s32_fifty : NULL); - ImGui::DragScalar("drag u32", ImGuiDataType_U32, &u32_v, drag_speed, drag_clamp ? &u32_zero : NULL, drag_clamp ? &u32_fifty : NULL, "%u ms"); - ImGui::DragScalar("drag s64", ImGuiDataType_S64, &s64_v, drag_speed, drag_clamp ? &s64_zero : NULL, drag_clamp ? &s64_fifty : NULL); - ImGui::DragScalar("drag u64", ImGuiDataType_U64, &u64_v, drag_speed, drag_clamp ? &u64_zero : NULL, drag_clamp ? &u64_fifty : NULL); - ImGui::DragScalar("drag float", ImGuiDataType_Float, &f32_v, 0.005f, &f32_zero, &f32_one, "%f", 1.0f); - ImGui::DragScalar("drag float ^2", ImGuiDataType_Float, &f32_v, 0.005f, &f32_zero, &f32_one, "%f", 2.0f); ImGui::SameLine(); ShowHelpMarker("You can use the 'power' parameter to increase tweaking precision on one side of the range."); - ImGui::DragScalar("drag double", ImGuiDataType_Double, &f64_v, 0.0005f, &f64_zero, NULL, "%.10f grams", 1.0f); - ImGui::DragScalar("drag double ^2", ImGuiDataType_Double, &f64_v, 0.0005f, &f64_zero, &f64_one, "0 < %.10f < 1", 2.0f); - - ImGui::Text("Sliders"); - ImGui::SliderScalar("slider s32 low", ImGuiDataType_S32, &s32_v, &s32_zero, &s32_fifty,"%d"); - ImGui::SliderScalar("slider s32 high", ImGuiDataType_S32, &s32_v, &s32_hi_a, &s32_hi_b, "%d"); - ImGui::SliderScalar("slider s32 full", ImGuiDataType_S32, &s32_v, &s32_min, &s32_max, "%d"); - ImGui::SliderScalar("slider u32 low", ImGuiDataType_U32, &u32_v, &u32_zero, &u32_fifty,"%u"); - ImGui::SliderScalar("slider u32 high", ImGuiDataType_U32, &u32_v, &u32_hi_a, &u32_hi_b, "%u"); - ImGui::SliderScalar("slider u32 full", ImGuiDataType_U32, &u32_v, &u32_min, &u32_max, "%u"); - ImGui::SliderScalar("slider s64 low", ImGuiDataType_S64, &s64_v, &s64_zero, &s64_fifty,"%I64d"); - ImGui::SliderScalar("slider s64 high", ImGuiDataType_S64, &s64_v, &s64_hi_a, &s64_hi_b, "%I64d"); - ImGui::SliderScalar("slider s64 full", ImGuiDataType_S64, &s64_v, &s64_min, &s64_max, "%I64d"); - ImGui::SliderScalar("slider u64 low", ImGuiDataType_U64, &u64_v, &u64_zero, &u64_fifty,"%I64u ms"); - ImGui::SliderScalar("slider u64 high", ImGuiDataType_U64, &u64_v, &u64_hi_a, &u64_hi_b, "%I64u ms"); - ImGui::SliderScalar("slider u64 full", ImGuiDataType_U64, &u64_v, &u64_min, &u64_max, "%I64u ms"); - ImGui::SliderScalar("slider float low", ImGuiDataType_Float, &f32_v, &f32_zero, &f32_one); - ImGui::SliderScalar("slider float low^2", ImGuiDataType_Float, &f32_v, &f32_zero, &f32_one, "%.10f", 2.0f); - ImGui::SliderScalar("slider float high", ImGuiDataType_Float, &f32_v, &f32_lo_a, &f32_hi_a, "%e"); - ImGui::SliderScalar("slider double low", ImGuiDataType_Double, &f64_v, &f64_zero, &f64_one, "%.10f grams", 1.0f); - ImGui::SliderScalar("slider double low^2",ImGuiDataType_Double, &f64_v, &f64_zero, &f64_one, "%.10f", 2.0f); - ImGui::SliderScalar("slider double high", ImGuiDataType_Double, &f64_v, &f64_lo_a, &f64_hi_a, "%e grams", 1.0f); - - static bool inputs_step = true; - ImGui::Text("Inputs"); - ImGui::Checkbox("Show step buttons", &inputs_step); - ImGui::InputScalar("input s32", ImGuiDataType_S32, &s32_v, inputs_step ? &s32_one : NULL, NULL, "%d"); - ImGui::InputScalar("input s32 hex", ImGuiDataType_S32, &s32_v, inputs_step ? &s32_one : NULL, NULL, "%08X", ImGuiInputTextFlags_CharsHexadecimal); - ImGui::InputScalar("input u32", ImGuiDataType_U32, &u32_v, inputs_step ? &u32_one : NULL, NULL, "%u"); - ImGui::InputScalar("input u32 hex", ImGuiDataType_U32, &u32_v, inputs_step ? &u32_one : NULL, NULL, "%08X", ImGuiInputTextFlags_CharsHexadecimal); - ImGui::InputScalar("input s64", ImGuiDataType_S64, &s64_v, inputs_step ? &s64_one : NULL); - ImGui::InputScalar("input u64", ImGuiDataType_U64, &u64_v, inputs_step ? &u64_one : NULL); - ImGui::InputScalar("input float", ImGuiDataType_Float, &f32_v, inputs_step ? &f32_one : NULL); - ImGui::InputScalar("input double", ImGuiDataType_Double, &f64_v, inputs_step ? &f64_one : NULL); + if (ImGui::TreeNode("Data Types")) + { + // The DragScalar/InputScalar/SliderScalar functions allow various data types: signed/unsigned int/long long and float/double + // To avoid polluting the public API with all possible combinations, we use the ImGuiDataType enum to pass the type, + // and passing all arguments by address. + // This is the reason the test code below creates local variables to hold "zero" "one" etc. for each types. + // In practice, if you frequently use a given type that is not covered by the normal API entry points, you can wrap it + // yourself inside a 1 line function which can take typed argument as value instead of void*, and then pass their address + // to the generic function. For example: + // bool MySliderU64(const char *label, u64* value, u64 min = 0, u64 max = 0, const char* format = "%lld") + // { + // return SliderScalar(label, ImGuiDataType_U64, value, &min, &max, format); + // } + + // Limits (as helper variables that we can take the address of) + // Note that the SliderScalar function has a maximum usable range of half the natural type maximum, hence the /2 below. + #ifndef LLONG_MIN + ImS64 LLONG_MIN = -9223372036854775807LL - 1; + ImS64 LLONG_MAX = 9223372036854775807LL; + ImU64 ULLONG_MAX = (2ULL * 9223372036854775807LL + 1); + #endif + const ImS32 s32_zero = 0, s32_one = 1, s32_fifty = 50, s32_min = INT_MIN/2, s32_max = INT_MAX/2, s32_hi_a = INT_MAX/2 - 100, s32_hi_b = INT_MAX/2; + const ImU32 u32_zero = 0, u32_one = 1, u32_fifty = 50, u32_min = 0, u32_max = UINT_MAX/2, u32_hi_a = UINT_MAX/2 - 100, u32_hi_b = UINT_MAX/2; + const ImS64 s64_zero = 0, s64_one = 1, s64_fifty = 50, s64_min = LLONG_MIN/2, s64_max = LLONG_MAX/2, s64_hi_a = LLONG_MAX/2 - 100, s64_hi_b = LLONG_MAX/2; + const ImU64 u64_zero = 0, u64_one = 1, u64_fifty = 50, u64_min = 0, u64_max = ULLONG_MAX/2, u64_hi_a = ULLONG_MAX/2 - 100, u64_hi_b = ULLONG_MAX/2; + const float f32_zero = 0.f, f32_one = 1.f, f32_lo_a = -10000000000.0f, f32_hi_a = +10000000000.0f; + const double f64_zero = 0., f64_one = 1., f64_lo_a = -1000000000000000.0, f64_hi_a = +1000000000000000.0; + + // State + static ImS32 s32_v = -1; + static ImU32 u32_v = (ImU32)-1; + static ImS64 s64_v = -1; + static ImU64 u64_v = (ImU64)-1; + static float f32_v = 0.123f; + static double f64_v = 90000.01234567890123456789; + + const float drag_speed = 0.2f; + static bool drag_clamp = false; + ImGui::Text("Drags:"); + ImGui::Checkbox("Clamp integers to 0..50", &drag_clamp); ImGui::SameLine(); ShowHelpMarker("As with every widgets in dear imgui, we never modify values unless there is a user interaction.\nYou can override the clamping limits by using CTRL+Click to input a value."); + ImGui::DragScalar("drag s32", ImGuiDataType_S32, &s32_v, drag_speed, drag_clamp ? &s32_zero : NULL, drag_clamp ? &s32_fifty : NULL); + ImGui::DragScalar("drag u32", ImGuiDataType_U32, &u32_v, drag_speed, drag_clamp ? &u32_zero : NULL, drag_clamp ? &u32_fifty : NULL, "%u ms"); + ImGui::DragScalar("drag s64", ImGuiDataType_S64, &s64_v, drag_speed, drag_clamp ? &s64_zero : NULL, drag_clamp ? &s64_fifty : NULL); + ImGui::DragScalar("drag u64", ImGuiDataType_U64, &u64_v, drag_speed, drag_clamp ? &u64_zero : NULL, drag_clamp ? &u64_fifty : NULL); + ImGui::DragScalar("drag float", ImGuiDataType_Float, &f32_v, 0.005f, &f32_zero, &f32_one, "%f", 1.0f); + ImGui::DragScalar("drag float ^2", ImGuiDataType_Float, &f32_v, 0.005f, &f32_zero, &f32_one, "%f", 2.0f); ImGui::SameLine(); ShowHelpMarker("You can use the 'power' parameter to increase tweaking precision on one side of the range."); + ImGui::DragScalar("drag double", ImGuiDataType_Double, &f64_v, 0.0005f, &f64_zero, NULL, "%.10f grams", 1.0f); + ImGui::DragScalar("drag double ^2", ImGuiDataType_Double, &f64_v, 0.0005f, &f64_zero, &f64_one, "0 < %.10f < 1", 2.0f); + + ImGui::Text("Sliders"); + ImGui::SliderScalar("slider s32 low", ImGuiDataType_S32, &s32_v, &s32_zero, &s32_fifty,"%d"); + ImGui::SliderScalar("slider s32 high", ImGuiDataType_S32, &s32_v, &s32_hi_a, &s32_hi_b, "%d"); + ImGui::SliderScalar("slider s32 full", ImGuiDataType_S32, &s32_v, &s32_min, &s32_max, "%d"); + ImGui::SliderScalar("slider u32 low", ImGuiDataType_U32, &u32_v, &u32_zero, &u32_fifty,"%u"); + ImGui::SliderScalar("slider u32 high", ImGuiDataType_U32, &u32_v, &u32_hi_a, &u32_hi_b, "%u"); + ImGui::SliderScalar("slider u32 full", ImGuiDataType_U32, &u32_v, &u32_min, &u32_max, "%u"); + ImGui::SliderScalar("slider s64 low", ImGuiDataType_S64, &s64_v, &s64_zero, &s64_fifty,"%I64d"); + ImGui::SliderScalar("slider s64 high", ImGuiDataType_S64, &s64_v, &s64_hi_a, &s64_hi_b, "%I64d"); + ImGui::SliderScalar("slider s64 full", ImGuiDataType_S64, &s64_v, &s64_min, &s64_max, "%I64d"); + ImGui::SliderScalar("slider u64 low", ImGuiDataType_U64, &u64_v, &u64_zero, &u64_fifty,"%I64u ms"); + ImGui::SliderScalar("slider u64 high", ImGuiDataType_U64, &u64_v, &u64_hi_a, &u64_hi_b, "%I64u ms"); + ImGui::SliderScalar("slider u64 full", ImGuiDataType_U64, &u64_v, &u64_min, &u64_max, "%I64u ms"); + ImGui::SliderScalar("slider float low", ImGuiDataType_Float, &f32_v, &f32_zero, &f32_one); + ImGui::SliderScalar("slider float low^2", ImGuiDataType_Float, &f32_v, &f32_zero, &f32_one, "%.10f", 2.0f); + ImGui::SliderScalar("slider float high", ImGuiDataType_Float, &f32_v, &f32_lo_a, &f32_hi_a, "%e"); + ImGui::SliderScalar("slider double low", ImGuiDataType_Double, &f64_v, &f64_zero, &f64_one, "%.10f grams", 1.0f); + ImGui::SliderScalar("slider double low^2",ImGuiDataType_Double, &f64_v, &f64_zero, &f64_one, "%.10f", 2.0f); + ImGui::SliderScalar("slider double high", ImGuiDataType_Double, &f64_v, &f64_lo_a, &f64_hi_a, "%e grams", 1.0f); + + static bool inputs_step = true; + ImGui::Text("Inputs"); + ImGui::Checkbox("Show step buttons", &inputs_step); + ImGui::InputScalar("input s32", ImGuiDataType_S32, &s32_v, inputs_step ? &s32_one : NULL, NULL, "%d"); + ImGui::InputScalar("input s32 hex", ImGuiDataType_S32, &s32_v, inputs_step ? &s32_one : NULL, NULL, "%08X", ImGuiInputTextFlags_CharsHexadecimal); + ImGui::InputScalar("input u32", ImGuiDataType_U32, &u32_v, inputs_step ? &u32_one : NULL, NULL, "%u"); + ImGui::InputScalar("input u32 hex", ImGuiDataType_U32, &u32_v, inputs_step ? &u32_one : NULL, NULL, "%08X", ImGuiInputTextFlags_CharsHexadecimal); + ImGui::InputScalar("input s64", ImGuiDataType_S64, &s64_v, inputs_step ? &s64_one : NULL); + ImGui::InputScalar("input u64", ImGuiDataType_U64, &u64_v, inputs_step ? &u64_one : NULL); + ImGui::InputScalar("input float", ImGuiDataType_Float, &f32_v, inputs_step ? &f32_one : NULL); + ImGui::InputScalar("input double", ImGuiDataType_Double, &f64_v, inputs_step ? &f64_one : NULL); - ImGui::TreePop(); - } + ImGui::TreePop(); + } - if (ImGui::TreeNode("Multi-component Widgets")) - { - static float vec4f[4] = { 0.10f, 0.20f, 0.30f, 0.44f }; - static int vec4i[4] = { 1, 5, 100, 255 }; + if (ImGui::TreeNode("Multi-component Widgets")) + { + static float vec4f[4] = { 0.10f, 0.20f, 0.30f, 0.44f }; + static int vec4i[4] = { 1, 5, 100, 255 }; + + ImGui::InputFloat2("input float2", vec4f); + ImGui::DragFloat2("drag float2", vec4f, 0.01f, 0.0f, 1.0f); + ImGui::SliderFloat2("slider float2", vec4f, 0.0f, 1.0f); + ImGui::InputInt2("input int2", vec4i); + ImGui::DragInt2("drag int2", vec4i, 1, 0, 255); + ImGui::SliderInt2("slider int2", vec4i, 0, 255); + ImGui::Spacing(); + + ImGui::InputFloat3("input float3", vec4f); + ImGui::DragFloat3("drag float3", vec4f, 0.01f, 0.0f, 1.0f); + ImGui::SliderFloat3("slider float3", vec4f, 0.0f, 1.0f); + ImGui::InputInt3("input int3", vec4i); + ImGui::DragInt3("drag int3", vec4i, 1, 0, 255); + ImGui::SliderInt3("slider int3", vec4i, 0, 255); + ImGui::Spacing(); + + ImGui::InputFloat4("input float4", vec4f); + ImGui::DragFloat4("drag float4", vec4f, 0.01f, 0.0f, 1.0f); + ImGui::SliderFloat4("slider float4", vec4f, 0.0f, 1.0f); + ImGui::InputInt4("input int4", vec4i); + ImGui::DragInt4("drag int4", vec4i, 1, 0, 255); + ImGui::SliderInt4("slider int4", vec4i, 0, 255); - ImGui::InputFloat2("input float2", vec4f); - ImGui::DragFloat2("drag float2", vec4f, 0.01f, 0.0f, 1.0f); - ImGui::SliderFloat2("slider float2", vec4f, 0.0f, 1.0f); - ImGui::InputInt2("input int2", vec4i); - ImGui::DragInt2("drag int2", vec4i, 1, 0, 255); - ImGui::SliderInt2("slider int2", vec4i, 0, 255); - ImGui::Spacing(); + ImGui::TreePop(); + } - ImGui::InputFloat3("input float3", vec4f); - ImGui::DragFloat3("drag float3", vec4f, 0.01f, 0.0f, 1.0f); - ImGui::SliderFloat3("slider float3", vec4f, 0.0f, 1.0f); - ImGui::InputInt3("input int3", vec4i); - ImGui::DragInt3("drag int3", vec4i, 1, 0, 255); - ImGui::SliderInt3("slider int3", vec4i, 0, 255); - ImGui::Spacing(); + if (ImGui::TreeNode("Vertical Sliders")) + { + const float spacing = 4; + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(spacing, spacing)); - ImGui::InputFloat4("input float4", vec4f); - ImGui::DragFloat4("drag float4", vec4f, 0.01f, 0.0f, 1.0f); - ImGui::SliderFloat4("slider float4", vec4f, 0.0f, 1.0f); - ImGui::InputInt4("input int4", vec4i); - ImGui::DragInt4("drag int4", vec4i, 1, 0, 255); - ImGui::SliderInt4("slider int4", vec4i, 0, 255); + static int int_value = 0; + ImGui::VSliderInt("##int", ImVec2(18,160), &int_value, 0, 5); + ImGui::SameLine(); - ImGui::TreePop(); + static float values[7] = { 0.0f, 0.60f, 0.35f, 0.9f, 0.70f, 0.20f, 0.0f }; + ImGui::PushID("set1"); + for (int i = 0; i < 7; i++) + { + if (i > 0) ImGui::SameLine(); + ImGui::PushID(i); + ImGui::PushStyleColor(ImGuiCol_FrameBg, (ImVec4)ImColor::HSV(i/7.0f, 0.5f, 0.5f)); + ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, (ImVec4)ImColor::HSV(i/7.0f, 0.6f, 0.5f)); + ImGui::PushStyleColor(ImGuiCol_FrameBgActive, (ImVec4)ImColor::HSV(i/7.0f, 0.7f, 0.5f)); + ImGui::PushStyleColor(ImGuiCol_SliderGrab, (ImVec4)ImColor::HSV(i/7.0f, 0.9f, 0.9f)); + ImGui::VSliderFloat("##v", ImVec2(18,160), &values[i], 0.0f, 1.0f, ""); + if (ImGui::IsItemActive() || ImGui::IsItemHovered()) + ImGui::SetTooltip("%.3f", values[i]); + ImGui::PopStyleColor(4); + ImGui::PopID(); } + ImGui::PopID(); - if (ImGui::TreeNode("Vertical Sliders")) + ImGui::SameLine(); + ImGui::PushID("set2"); + static float values2[4] = { 0.20f, 0.80f, 0.40f, 0.25f }; + const int rows = 3; + const ImVec2 small_slider_size(18, (160.0f-(rows-1)*spacing)/rows); + for (int nx = 0; nx < 4; nx++) { - const float spacing = 4; - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(spacing, spacing)); - - static int int_value = 0; - ImGui::VSliderInt("##int", ImVec2(18,160), &int_value, 0, 5); - ImGui::SameLine(); - - static float values[7] = { 0.0f, 0.60f, 0.35f, 0.9f, 0.70f, 0.20f, 0.0f }; - ImGui::PushID("set1"); - for (int i = 0; i < 7; i++) + if (nx > 0) ImGui::SameLine(); + ImGui::BeginGroup(); + for (int ny = 0; ny < rows; ny++) { - if (i > 0) ImGui::SameLine(); - ImGui::PushID(i); - ImGui::PushStyleColor(ImGuiCol_FrameBg, (ImVec4)ImColor::HSV(i/7.0f, 0.5f, 0.5f)); - ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, (ImVec4)ImColor::HSV(i/7.0f, 0.6f, 0.5f)); - ImGui::PushStyleColor(ImGuiCol_FrameBgActive, (ImVec4)ImColor::HSV(i/7.0f, 0.7f, 0.5f)); - ImGui::PushStyleColor(ImGuiCol_SliderGrab, (ImVec4)ImColor::HSV(i/7.0f, 0.9f, 0.9f)); - ImGui::VSliderFloat("##v", ImVec2(18,160), &values[i], 0.0f, 1.0f, ""); + ImGui::PushID(nx*rows+ny); + ImGui::VSliderFloat("##v", small_slider_size, &values2[nx], 0.0f, 1.0f, ""); if (ImGui::IsItemActive() || ImGui::IsItemHovered()) - ImGui::SetTooltip("%.3f", values[i]); - ImGui::PopStyleColor(4); + ImGui::SetTooltip("%.3f", values2[nx]); ImGui::PopID(); } - ImGui::PopID(); + ImGui::EndGroup(); + } + ImGui::PopID(); - ImGui::SameLine(); - ImGui::PushID("set2"); - static float values2[4] = { 0.20f, 0.80f, 0.40f, 0.25f }; - const int rows = 3; - const ImVec2 small_slider_size(18, (160.0f-(rows-1)*spacing)/rows); - for (int nx = 0; nx < 4; nx++) - { - if (nx > 0) ImGui::SameLine(); - ImGui::BeginGroup(); - for (int ny = 0; ny < rows; ny++) - { - ImGui::PushID(nx*rows+ny); - ImGui::VSliderFloat("##v", small_slider_size, &values2[nx], 0.0f, 1.0f, ""); - if (ImGui::IsItemActive() || ImGui::IsItemHovered()) - ImGui::SetTooltip("%.3f", values2[nx]); - ImGui::PopID(); - } - ImGui::EndGroup(); - } + ImGui::SameLine(); + ImGui::PushID("set3"); + for (int i = 0; i < 4; i++) + { + if (i > 0) ImGui::SameLine(); + ImGui::PushID(i); + ImGui::PushStyleVar(ImGuiStyleVar_GrabMinSize, 40); + ImGui::VSliderFloat("##v", ImVec2(40,160), &values[i], 0.0f, 1.0f, "%.2f\nsec"); + ImGui::PopStyleVar(); ImGui::PopID(); + } + ImGui::PopID(); + ImGui::PopStyleVar(); + ImGui::TreePop(); + } - ImGui::SameLine(); - ImGui::PushID("set3"); - for (int i = 0; i < 4; i++) - { - if (i > 0) ImGui::SameLine(); - ImGui::PushID(i); - ImGui::PushStyleVar(ImGuiStyleVar_GrabMinSize, 40); - ImGui::VSliderFloat("##v", ImVec2(40,160), &values[i], 0.0f, 1.0f, "%.2f\nsec"); - ImGui::PopStyleVar(); - ImGui::PopID(); - } - ImGui::PopID(); - ImGui::PopStyleVar(); - ImGui::TreePop(); + if (ImGui::TreeNode("Drag and Drop")) + { + { + // ColorEdit widgets automatically act as drag source and drag target. + // They are using standardized payload strings IMGUI_PAYLOAD_TYPE_COLOR_3F and IMGUI_PAYLOAD_TYPE_COLOR_4F to allow your own widgets + // to use colors in their drag and drop interaction. Also see the demo in Color Picker -> Palette demo. + ImGui::BulletText("Drag and drop in standard widgets"); + ImGui::Indent(); + static float col1[3] = { 1.0f,0.0f,0.2f }; + static float col2[4] = { 0.4f,0.7f,0.0f,0.5f }; + ImGui::ColorEdit3("color 1", col1); + ImGui::ColorEdit4("color 2", col2); + ImGui::Unindent(); } - if (ImGui::TreeNode("Drag and Drop")) { + ImGui::BulletText("Drag and drop to copy/swap items"); + ImGui::Indent(); + enum Mode { - // ColorEdit widgets automatically act as drag source and drag target. - // They are using standardized payload strings IMGUI_PAYLOAD_TYPE_COLOR_3F and IMGUI_PAYLOAD_TYPE_COLOR_4F to allow your own widgets - // to use colors in their drag and drop interaction. Also see the demo in Color Picker -> Palette demo. - ImGui::BulletText("Drag and drop in standard widgets"); - ImGui::Indent(); - static float col1[3] = { 1.0f,0.0f,0.2f }; - static float col2[4] = { 0.4f,0.7f,0.0f,0.5f }; - ImGui::ColorEdit3("color 1", col1); - ImGui::ColorEdit4("color 2", col2); - ImGui::Unindent(); - } - + Mode_Copy, + Mode_Move, + Mode_Swap + }; + static int mode = 0; + if (ImGui::RadioButton("Copy", mode == Mode_Copy)) { mode = Mode_Copy; } ImGui::SameLine(); + if (ImGui::RadioButton("Move", mode == Mode_Move)) { mode = Mode_Move; } ImGui::SameLine(); + if (ImGui::RadioButton("Swap", mode == Mode_Swap)) { mode = Mode_Swap; } + static const char* names[9] = { "Bobby", "Beatrice", "Betty", "Brianna", "Barry", "Bernard", "Bibi", "Blaine", "Bryn" }; + for (int n = 0; n < IM_ARRAYSIZE(names); n++) { - ImGui::BulletText("Drag and drop to copy/swap items"); - ImGui::Indent(); - enum Mode + ImGui::PushID(n); + if ((n % 3) != 0) + ImGui::SameLine(); + ImGui::Button(names[n], ImVec2(60,60)); + + // Our buttons are both drag sources and drag targets here! + if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) { - Mode_Copy, - Mode_Move, - Mode_Swap - }; - static int mode = 0; - if (ImGui::RadioButton("Copy", mode == Mode_Copy)) { mode = Mode_Copy; } ImGui::SameLine(); - if (ImGui::RadioButton("Move", mode == Mode_Move)) { mode = Mode_Move; } ImGui::SameLine(); - if (ImGui::RadioButton("Swap", mode == Mode_Swap)) { mode = Mode_Swap; } - static const char* names[9] = { "Bobby", "Beatrice", "Betty", "Brianna", "Barry", "Bernard", "Bibi", "Blaine", "Bryn" }; - for (int n = 0; n < IM_ARRAYSIZE(names); n++) + ImGui::SetDragDropPayload("DND_DEMO_CELL", &n, sizeof(int)); // Set payload to carry the index of our item (could be anything) + if (mode == Mode_Copy) { ImGui::Text("Copy %s", names[n]); } // Display preview (could be anything, e.g. when dragging an image we could decide to display the filename and a small preview of the image, etc.) + if (mode == Mode_Move) { ImGui::Text("Move %s", names[n]); } + if (mode == Mode_Swap) { ImGui::Text("Swap %s", names[n]); } + ImGui::EndDragDropSource(); + } + if (ImGui::BeginDragDropTarget()) { - ImGui::PushID(n); - if ((n % 3) != 0) - ImGui::SameLine(); - ImGui::Button(names[n], ImVec2(60,60)); - - // Our buttons are both drag sources and drag targets here! - if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("DND_DEMO_CELL")) { - ImGui::SetDragDropPayload("DND_DEMO_CELL", &n, sizeof(int)); // Set payload to carry the index of our item (could be anything) - if (mode == Mode_Copy) { ImGui::Text("Copy %s", names[n]); } // Display preview (could be anything, e.g. when dragging an image we could decide to display the filename and a small preview of the image, etc.) - if (mode == Mode_Move) { ImGui::Text("Move %s", names[n]); } - if (mode == Mode_Swap) { ImGui::Text("Swap %s", names[n]); } - ImGui::EndDragDropSource(); - } - if (ImGui::BeginDragDropTarget()) - { - if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("DND_DEMO_CELL")) + IM_ASSERT(payload->DataSize == sizeof(int)); + int payload_n = *(const int*)payload->Data; + if (mode == Mode_Copy) { - IM_ASSERT(payload->DataSize == sizeof(int)); - int payload_n = *(const int*)payload->Data; - if (mode == Mode_Copy) - { - names[n] = names[payload_n]; - } - if (mode == Mode_Move) - { - names[n] = names[payload_n]; - names[payload_n] = ""; - } - if (mode == Mode_Swap) - { - const char* tmp = names[n]; - names[n] = names[payload_n]; - names[payload_n] = tmp; - } + names[n] = names[payload_n]; + } + if (mode == Mode_Move) + { + names[n] = names[payload_n]; + names[payload_n] = ""; + } + if (mode == Mode_Swap) + { + const char* tmp = names[n]; + names[n] = names[payload_n]; + names[payload_n] = tmp; } - ImGui::EndDragDropTarget(); } - ImGui::PopID(); + ImGui::EndDragDropTarget(); } - ImGui::Unindent(); + ImGui::PopID(); } - - ImGui::TreePop(); + ImGui::Unindent(); } - if (ImGui::TreeNode("Querying Status (Active/Focused/Hovered etc.)")) - { - // Display the value of IsItemHovered() and other common item state functions. Note that the flags can be combined. - // (because BulletText is an item itself and that would affect the output of IsItemHovered() we pass all state in a single call to simplify the code). - static int item_type = 1; - static bool b = false; - static float col4f[4] = { 1.0f, 0.5, 0.0f, 1.0f }; - ImGui::RadioButton("Text", &item_type, 0); - ImGui::RadioButton("Button", &item_type, 1); - ImGui::RadioButton("CheckBox", &item_type, 2); - ImGui::RadioButton("SliderFloat", &item_type, 3); - ImGui::RadioButton("ColorEdit4", &item_type, 4); - ImGui::RadioButton("ListBox", &item_type, 5); - ImGui::Separator(); - bool ret = false; - if (item_type == 0) { ImGui::Text("ITEM: Text"); } // Testing text items with no identifier/interaction - if (item_type == 1) { ret = ImGui::Button("ITEM: Button"); } // Testing button - if (item_type == 2) { ret = ImGui::Checkbox("ITEM: CheckBox", &b); } // Testing checkbox - if (item_type == 3) { ret = ImGui::SliderFloat("ITEM: SliderFloat", &col4f[0], 0.0f, 1.0f); } // Testing basic item - if (item_type == 4) { ret = ImGui::ColorEdit4("ITEM: ColorEdit4", col4f); } // Testing multi-component items (IsItemXXX flags are reported merged) - if (item_type == 5) { const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::ListBox("ITEM: ListBox", ¤t, items, IM_ARRAYSIZE(items), IM_ARRAYSIZE(items)); } - ImGui::BulletText( - "Return value = %d\n" - "IsItemFocused() = %d\n" - "IsItemHovered() = %d\n" - "IsItemHovered(_AllowWhenBlockedByPopup) = %d\n" - "IsItemHovered(_AllowWhenBlockedByActiveItem) = %d\n" - "IsItemHovered(_AllowWhenOverlapped) = %d\n" - "IsItemHovered(_RectOnly) = %d\n" - "IsItemActive() = %d\n" - "IsItemEdited() = %d\n" - "IsItemDeactivated() = %d\n" - "IsItemDeactivatedEdit() = %d\n" - "IsItemVisible() = %d\n" - "GetItemRectMin() = (%.1f, %.1f)\n" - "GetItemRectMax() = (%.1f, %.1f)\n" - "GetItemRectSize() = (%.1f, %.1f)", - ret, - ImGui::IsItemFocused(), - ImGui::IsItemHovered(), - ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup), - ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem), - ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenOverlapped), - ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly), - ImGui::IsItemActive(), - ImGui::IsItemEdited(), - ImGui::IsItemDeactivated(), - ImGui::IsItemDeactivatedAfterEdit(), - ImGui::IsItemVisible(), - ImGui::GetItemRectMin().x, ImGui::GetItemRectMin().y, - ImGui::GetItemRectMax().x, ImGui::GetItemRectMax().y, - ImGui::GetItemRectSize().x, ImGui::GetItemRectSize().y - ); - - static bool embed_all_inside_a_child_window = false; - ImGui::Checkbox("Embed everything inside a child window (for additional testing)", &embed_all_inside_a_child_window); - if (embed_all_inside_a_child_window) - ImGui::BeginChild("outer_child", ImVec2(0, ImGui::GetFontSize() * 20), true); - - // Testing IsWindowFocused() function with its various flags. Note that the flags can be combined. - ImGui::BulletText( - "IsWindowFocused() = %d\n" - "IsWindowFocused(_ChildWindows) = %d\n" - "IsWindowFocused(_ChildWindows|_RootWindow) = %d\n" - "IsWindowFocused(_RootWindow) = %d\n" - "IsWindowFocused(_AnyWindow) = %d\n", - ImGui::IsWindowFocused(), - ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows), - ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow), - ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow), - ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow)); - - // Testing IsWindowHovered() function with its various flags. Note that the flags can be combined. - ImGui::BulletText( - "IsWindowHovered() = %d\n" - "IsWindowHovered(_AllowWhenBlockedByPopup) = %d\n" - "IsWindowHovered(_AllowWhenBlockedByActiveItem) = %d\n" - "IsWindowHovered(_ChildWindows) = %d\n" - "IsWindowHovered(_ChildWindows|_RootWindow) = %d\n" - "IsWindowHovered(_RootWindow) = %d\n" - "IsWindowHovered(_AnyWindow) = %d\n", - ImGui::IsWindowHovered(), - ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup), - ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem), - ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows), - ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow), - ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow), - ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow)); - - ImGui::BeginChild("child", ImVec2(0, 50), true); - ImGui::Text("This is another child window for testing with the _ChildWindows flag."); + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Querying Status (Active/Focused/Hovered etc.)")) + { + // Display the value of IsItemHovered() and other common item state functions. Note that the flags can be combined. + // (because BulletText is an item itself and that would affect the output of IsItemHovered() we pass all state in a single call to simplify the code). + static int item_type = 1; + static bool b = false; + static float col4f[4] = { 1.0f, 0.5, 0.0f, 1.0f }; + ImGui::RadioButton("Text", &item_type, 0); + ImGui::RadioButton("Button", &item_type, 1); + ImGui::RadioButton("CheckBox", &item_type, 2); + ImGui::RadioButton("SliderFloat", &item_type, 3); + ImGui::RadioButton("ColorEdit4", &item_type, 4); + ImGui::RadioButton("ListBox", &item_type, 5); + ImGui::Separator(); + bool ret = false; + if (item_type == 0) { ImGui::Text("ITEM: Text"); } // Testing text items with no identifier/interaction + if (item_type == 1) { ret = ImGui::Button("ITEM: Button"); } // Testing button + if (item_type == 2) { ret = ImGui::Checkbox("ITEM: CheckBox", &b); } // Testing checkbox + if (item_type == 3) { ret = ImGui::SliderFloat("ITEM: SliderFloat", &col4f[0], 0.0f, 1.0f); } // Testing basic item + if (item_type == 4) { ret = ImGui::ColorEdit4("ITEM: ColorEdit4", col4f); } // Testing multi-component items (IsItemXXX flags are reported merged) + if (item_type == 5) { const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::ListBox("ITEM: ListBox", ¤t, items, IM_ARRAYSIZE(items), IM_ARRAYSIZE(items)); } + ImGui::BulletText( + "Return value = %d\n" + "IsItemFocused() = %d\n" + "IsItemHovered() = %d\n" + "IsItemHovered(_AllowWhenBlockedByPopup) = %d\n" + "IsItemHovered(_AllowWhenBlockedByActiveItem) = %d\n" + "IsItemHovered(_AllowWhenOverlapped) = %d\n" + "IsItemHovered(_RectOnly) = %d\n" + "IsItemActive() = %d\n" + "IsItemEdited() = %d\n" + "IsItemDeactivated() = %d\n" + "IsItemDeactivatedEdit() = %d\n" + "IsItemVisible() = %d\n" + "GetItemRectMin() = (%.1f, %.1f)\n" + "GetItemRectMax() = (%.1f, %.1f)\n" + "GetItemRectSize() = (%.1f, %.1f)", + ret, + ImGui::IsItemFocused(), + ImGui::IsItemHovered(), + ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup), + ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem), + ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenOverlapped), + ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly), + ImGui::IsItemActive(), + ImGui::IsItemEdited(), + ImGui::IsItemDeactivated(), + ImGui::IsItemDeactivatedAfterEdit(), + ImGui::IsItemVisible(), + ImGui::GetItemRectMin().x, ImGui::GetItemRectMin().y, + ImGui::GetItemRectMax().x, ImGui::GetItemRectMax().y, + ImGui::GetItemRectSize().x, ImGui::GetItemRectSize().y + ); + + static bool embed_all_inside_a_child_window = false; + ImGui::Checkbox("Embed everything inside a child window (for additional testing)", &embed_all_inside_a_child_window); + if (embed_all_inside_a_child_window) + ImGui::BeginChild("outer_child", ImVec2(0, ImGui::GetFontSize() * 20), true); + + // Testing IsWindowFocused() function with its various flags. Note that the flags can be combined. + ImGui::BulletText( + "IsWindowFocused() = %d\n" + "IsWindowFocused(_ChildWindows) = %d\n" + "IsWindowFocused(_ChildWindows|_RootWindow) = %d\n" + "IsWindowFocused(_RootWindow) = %d\n" + "IsWindowFocused(_AnyWindow) = %d\n", + ImGui::IsWindowFocused(), + ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows), + ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow), + ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow), + ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow)); + + // Testing IsWindowHovered() function with its various flags. Note that the flags can be combined. + ImGui::BulletText( + "IsWindowHovered() = %d\n" + "IsWindowHovered(_AllowWhenBlockedByPopup) = %d\n" + "IsWindowHovered(_AllowWhenBlockedByActiveItem) = %d\n" + "IsWindowHovered(_ChildWindows) = %d\n" + "IsWindowHovered(_ChildWindows|_RootWindow) = %d\n" + "IsWindowHovered(_RootWindow) = %d\n" + "IsWindowHovered(_AnyWindow) = %d\n", + ImGui::IsWindowHovered(), + ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup), + ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem), + ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows), + ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow), + ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow), + ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow)); + + ImGui::BeginChild("child", ImVec2(0, 50), true); + ImGui::Text("This is another child window for testing with the _ChildWindows flag."); + ImGui::EndChild(); + if (embed_all_inside_a_child_window) ImGui::EndChild(); - if (embed_all_inside_a_child_window) - EndChild(); - - // Calling IsItemHovered() after begin returns the hovered status of the title bar. - // This is useful in particular if you want to create a context menu (with BeginPopupContextItem) associated to the title bar of a window. - static bool test_window = false; - ImGui::Checkbox("Hovered/Active tests after Begin() for title bar testing", &test_window); - if (test_window) + + // Calling IsItemHovered() after begin returns the hovered status of the title bar. + // This is useful in particular if you want to create a context menu (with BeginPopupContextItem) associated to the title bar of a window. + static bool test_window = false; + ImGui::Checkbox("Hovered/Active tests after Begin() for title bar testing", &test_window); + if (test_window) + { + ImGui::Begin("Title bar Hovered/Active tests", &test_window); + if (ImGui::BeginPopupContextItem()) // <-- This is using IsItemHovered() { - ImGui::Begin("Title bar Hovered/Active tests", &test_window); - if (ImGui::BeginPopupContextItem()) // <-- This is using IsItemHovered() - { - if (ImGui::MenuItem("Close")) { test_window = false; } - ImGui::EndPopup(); - } - ImGui::Text( - "IsItemHovered() after begin = %d (== is title bar hovered)\n" - "IsItemActive() after begin = %d (== is window being clicked/moved)\n", - ImGui::IsItemHovered(), ImGui::IsItemActive()); - ImGui::End(); + if (ImGui::MenuItem("Close")) { test_window = false; } + ImGui::EndPopup(); } - - ImGui::TreePop(); + ImGui::Text( + "IsItemHovered() after begin = %d (== is title bar hovered)\n" + "IsItemActive() after begin = %d (== is window being clicked/moved)\n", + ImGui::IsItemHovered(), ImGui::IsItemActive()); + ImGui::End(); } + + ImGui::TreePop(); } +} + +static void ShowDemoWindowLayout() +{ + if (!ImGui::CollapsingHeader("Layout")) + return; - if (ImGui::CollapsingHeader("Layout")) + if (ImGui::TreeNode("Child regions")) { - if (ImGui::TreeNode("Child regions")) - { - static bool disable_mouse_wheel = false; - static bool disable_menu = false; - ImGui::Checkbox("Disable Mouse Wheel", &disable_mouse_wheel); - ImGui::Checkbox("Disable Menu", &disable_menu); + static bool disable_mouse_wheel = false; + static bool disable_menu = false; + ImGui::Checkbox("Disable Mouse Wheel", &disable_mouse_wheel); + ImGui::Checkbox("Disable Menu", &disable_menu); - static int line = 50; - bool goto_line = ImGui::Button("Goto"); - ImGui::SameLine(); - ImGui::PushItemWidth(100); - goto_line |= ImGui::InputInt("##Line", &line, 0, 0, ImGuiInputTextFlags_EnterReturnsTrue); - ImGui::PopItemWidth(); + static int line = 50; + bool goto_line = ImGui::Button("Goto"); + ImGui::SameLine(); + ImGui::PushItemWidth(100); + goto_line |= ImGui::InputInt("##Line", &line, 0, 0, ImGuiInputTextFlags_EnterReturnsTrue); + ImGui::PopItemWidth(); - // Child 1: no border, enable horizontal scrollbar + // Child 1: no border, enable horizontal scrollbar + { + ImGui::BeginChild("Child1", ImVec2(ImGui::GetWindowContentRegionWidth() * 0.5f, 300), false, ImGuiWindowFlags_HorizontalScrollbar | (disable_mouse_wheel ? ImGuiWindowFlags_NoScrollWithMouse : 0)); + for (int i = 0; i < 100; i++) { - ImGui::BeginChild("Child1", ImVec2(ImGui::GetWindowContentRegionWidth() * 0.5f, 300), false, ImGuiWindowFlags_HorizontalScrollbar | (disable_mouse_wheel ? ImGuiWindowFlags_NoScrollWithMouse : 0)); - for (int i = 0; i < 100; i++) - { - ImGui::Text("%04d: scrollable region", i); - if (goto_line && line == i) - ImGui::SetScrollHere(); - } - if (goto_line && line >= 100) - ImGui::SetScrollHere(); - ImGui::EndChild(); + ImGui::Text("%04d: scrollable region", i); + if (goto_line && line == i) + ImGui::SetScrollHereY(); } + if (goto_line && line >= 100) + ImGui::SetScrollHereY(); + ImGui::EndChild(); + } - ImGui::SameLine(); + ImGui::SameLine(); - // Child 2: rounded border + // Child 2: rounded border + { + ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 5.0f); + ImGui::BeginChild("Child2", ImVec2(0, 300), true, (disable_mouse_wheel ? ImGuiWindowFlags_NoScrollWithMouse : 0) | (disable_menu ? 0 : ImGuiWindowFlags_MenuBar)); + if (!disable_menu && ImGui::BeginMenuBar()) { - ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 5.0f); - ImGui::BeginChild("Child2", ImVec2(0,300), true, (disable_mouse_wheel ? ImGuiWindowFlags_NoScrollWithMouse : 0) | (disable_menu ? 0 : ImGuiWindowFlags_MenuBar)); - if (!disable_menu && ImGui::BeginMenuBar()) + if (ImGui::BeginMenu("Menu")) { - if (ImGui::BeginMenu("Menu")) - { - ShowExampleMenuFile(); - ImGui::EndMenu(); - } - ImGui::EndMenuBar(); - } - ImGui::Columns(2); - for (int i = 0; i < 100; i++) - { - char buf[32]; - sprintf(buf, "%03d", i); - ImGui::Button(buf, ImVec2(-1.0f, 0.0f)); - ImGui::NextColumn(); + ShowExampleMenuFile(); + ImGui::EndMenu(); } - ImGui::EndChild(); - ImGui::PopStyleVar(); + ImGui::EndMenuBar(); } - - ImGui::TreePop(); + ImGui::Columns(2); + for (int i = 0; i < 100; i++) + { + char buf[32]; + sprintf(buf, "%03d", i); + ImGui::Button(buf, ImVec2(-1.0f, 0.0f)); + ImGui::NextColumn(); + } + ImGui::EndChild(); + ImGui::PopStyleVar(); } - if (ImGui::TreeNode("Widgets Width")) - { - static float f = 0.0f; - ImGui::Text("PushItemWidth(100)"); - ImGui::SameLine(); ShowHelpMarker("Fixed width."); - ImGui::PushItemWidth(100); - ImGui::DragFloat("float##1", &f); - ImGui::PopItemWidth(); - - ImGui::Text("PushItemWidth(GetWindowWidth() * 0.5f)"); - ImGui::SameLine(); ShowHelpMarker("Half of window width."); - ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.5f); - ImGui::DragFloat("float##2", &f); - ImGui::PopItemWidth(); - - ImGui::Text("PushItemWidth(GetContentRegionAvailWidth() * 0.5f)"); - ImGui::SameLine(); ShowHelpMarker("Half of available width.\n(~ right-cursor_pos)\n(works within a column set)"); - ImGui::PushItemWidth(ImGui::GetContentRegionAvailWidth() * 0.5f); - ImGui::DragFloat("float##3", &f); - ImGui::PopItemWidth(); - - ImGui::Text("PushItemWidth(-100)"); - ImGui::SameLine(); ShowHelpMarker("Align to right edge minus 100"); - ImGui::PushItemWidth(-100); - ImGui::DragFloat("float##4", &f); - ImGui::PopItemWidth(); + ImGui::TreePop(); + } - ImGui::Text("PushItemWidth(-1)"); - ImGui::SameLine(); ShowHelpMarker("Align to right edge"); - ImGui::PushItemWidth(-1); - ImGui::DragFloat("float##5", &f); - ImGui::PopItemWidth(); + if (ImGui::TreeNode("Widgets Width")) + { + static float f = 0.0f; + ImGui::Text("PushItemWidth(100)"); + ImGui::SameLine(); ShowHelpMarker("Fixed width."); + ImGui::PushItemWidth(100); + ImGui::DragFloat("float##1", &f); + ImGui::PopItemWidth(); - ImGui::TreePop(); - } + ImGui::Text("PushItemWidth(GetWindowWidth() * 0.5f)"); + ImGui::SameLine(); ShowHelpMarker("Half of window width."); + ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.5f); + ImGui::DragFloat("float##2", &f); + ImGui::PopItemWidth(); - if (ImGui::TreeNode("Basic Horizontal Layout")) - { - ImGui::TextWrapped("(Use ImGui::SameLine() to keep adding items to the right of the preceding item)"); + ImGui::Text("PushItemWidth(GetContentRegionAvailWidth() * 0.5f)"); + ImGui::SameLine(); ShowHelpMarker("Half of available width.\n(~ right-cursor_pos)\n(works within a column set)"); + ImGui::PushItemWidth(ImGui::GetContentRegionAvailWidth() * 0.5f); + ImGui::DragFloat("float##3", &f); + ImGui::PopItemWidth(); - // Text - ImGui::Text("Two items: Hello"); ImGui::SameLine(); - ImGui::TextColored(ImVec4(1,1,0,1), "Sailor"); + ImGui::Text("PushItemWidth(-100)"); + ImGui::SameLine(); ShowHelpMarker("Align to right edge minus 100"); + ImGui::PushItemWidth(-100); + ImGui::DragFloat("float##4", &f); + ImGui::PopItemWidth(); - // Adjust spacing - ImGui::Text("More spacing: Hello"); ImGui::SameLine(0, 20); - ImGui::TextColored(ImVec4(1,1,0,1), "Sailor"); + ImGui::Text("PushItemWidth(-1)"); + ImGui::SameLine(); ShowHelpMarker("Align to right edge"); + ImGui::PushItemWidth(-1); + ImGui::DragFloat("float##5", &f); + ImGui::PopItemWidth(); - // Button - ImGui::AlignTextToFramePadding(); - ImGui::Text("Normal buttons"); ImGui::SameLine(); - ImGui::Button("Banana"); ImGui::SameLine(); - ImGui::Button("Apple"); ImGui::SameLine(); - ImGui::Button("Corniflower"); - - // Button - ImGui::Text("Small buttons"); ImGui::SameLine(); - ImGui::SmallButton("Like this one"); ImGui::SameLine(); - ImGui::Text("can fit within a text block."); - - // Aligned to arbitrary position. Easy/cheap column. - ImGui::Text("Aligned"); - ImGui::SameLine(150); ImGui::Text("x=150"); - ImGui::SameLine(300); ImGui::Text("x=300"); - ImGui::Text("Aligned"); - ImGui::SameLine(150); ImGui::SmallButton("x=150"); - ImGui::SameLine(300); ImGui::SmallButton("x=300"); - - // Checkbox - static bool c1=false,c2=false,c3=false,c4=false; - ImGui::Checkbox("My", &c1); ImGui::SameLine(); - ImGui::Checkbox("Tailor", &c2); ImGui::SameLine(); - ImGui::Checkbox("Is", &c3); ImGui::SameLine(); - ImGui::Checkbox("Rich", &c4); - - // Various - static float f0=1.0f, f1=2.0f, f2=3.0f; - ImGui::PushItemWidth(80); - const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD" }; - static int item = -1; - ImGui::Combo("Combo", &item, items, IM_ARRAYSIZE(items)); ImGui::SameLine(); - ImGui::SliderFloat("X", &f0, 0.0f,5.0f); ImGui::SameLine(); - ImGui::SliderFloat("Y", &f1, 0.0f,5.0f); ImGui::SameLine(); - ImGui::SliderFloat("Z", &f2, 0.0f,5.0f); - ImGui::PopItemWidth(); + ImGui::TreePop(); + } - ImGui::PushItemWidth(80); - ImGui::Text("Lists:"); - static int selection[4] = { 0, 1, 2, 3 }; - for (int i = 0; i < 4; i++) - { - if (i > 0) ImGui::SameLine(); - ImGui::PushID(i); - ImGui::ListBox("", &selection[i], items, IM_ARRAYSIZE(items)); - ImGui::PopID(); - //if (ImGui::IsItemHovered()) ImGui::SetTooltip("ListBox %d hovered", i); - } - ImGui::PopItemWidth(); + if (ImGui::TreeNode("Basic Horizontal Layout")) + { + ImGui::TextWrapped("(Use ImGui::SameLine() to keep adding items to the right of the preceding item)"); + + // Text + ImGui::Text("Two items: Hello"); ImGui::SameLine(); + ImGui::TextColored(ImVec4(1,1,0,1), "Sailor"); + + // Adjust spacing + ImGui::Text("More spacing: Hello"); ImGui::SameLine(0, 20); + ImGui::TextColored(ImVec4(1,1,0,1), "Sailor"); + + // Button + ImGui::AlignTextToFramePadding(); + ImGui::Text("Normal buttons"); ImGui::SameLine(); + ImGui::Button("Banana"); ImGui::SameLine(); + ImGui::Button("Apple"); ImGui::SameLine(); + ImGui::Button("Corniflower"); + + // Button + ImGui::Text("Small buttons"); ImGui::SameLine(); + ImGui::SmallButton("Like this one"); ImGui::SameLine(); + ImGui::Text("can fit within a text block."); + + // Aligned to arbitrary position. Easy/cheap column. + ImGui::Text("Aligned"); + ImGui::SameLine(150); ImGui::Text("x=150"); + ImGui::SameLine(300); ImGui::Text("x=300"); + ImGui::Text("Aligned"); + ImGui::SameLine(150); ImGui::SmallButton("x=150"); + ImGui::SameLine(300); ImGui::SmallButton("x=300"); + + // Checkbox + static bool c1 = false, c2 = false, c3 = false, c4 = false; + ImGui::Checkbox("My", &c1); ImGui::SameLine(); + ImGui::Checkbox("Tailor", &c2); ImGui::SameLine(); + ImGui::Checkbox("Is", &c3); ImGui::SameLine(); + ImGui::Checkbox("Rich", &c4); + + // Various + static float f0 = 1.0f, f1 = 2.0f, f2 = 3.0f; + ImGui::PushItemWidth(80); + const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD" }; + static int item = -1; + ImGui::Combo("Combo", &item, items, IM_ARRAYSIZE(items)); ImGui::SameLine(); + ImGui::SliderFloat("X", &f0, 0.0f, 5.0f); ImGui::SameLine(); + ImGui::SliderFloat("Y", &f1, 0.0f, 5.0f); ImGui::SameLine(); + ImGui::SliderFloat("Z", &f2, 0.0f, 5.0f); + ImGui::PopItemWidth(); - // Dummy - ImVec2 button_sz(40,40); - ImGui::Button("A", button_sz); ImGui::SameLine(); - ImGui::Dummy(button_sz); ImGui::SameLine(); - ImGui::Button("B", button_sz); - - // Manually wrapping (we should eventually provide this as an automatic layout feature, but for now you can do it manually) - ImGui::Text("Manually wrapping:"); - ImGuiStyle& style = ImGui::GetStyle(); - int buttons_count = 20; - float window_visible_x2 = ImGui::GetWindowPos().x + ImGui::GetWindowContentRegionMax().x; - for (int n = 0; n < buttons_count; n++) - { - ImGui::PushID(n); - ImGui::Button("Box", button_sz); - float last_button_x2 = ImGui::GetItemRectMax().x; - float next_button_x2 = last_button_x2 + style.ItemSpacing.x + button_sz.x; // Expected position if next button was on same line - if (n + 1 < buttons_count && next_button_x2 < window_visible_x2) - ImGui::SameLine(); - ImGui::PopID(); - } + ImGui::PushItemWidth(80); + ImGui::Text("Lists:"); + static int selection[4] = { 0, 1, 2, 3 }; + for (int i = 0; i < 4; i++) + { + if (i > 0) ImGui::SameLine(); + ImGui::PushID(i); + ImGui::ListBox("", &selection[i], items, IM_ARRAYSIZE(items)); + ImGui::PopID(); + //if (ImGui::IsItemHovered()) ImGui::SetTooltip("ListBox %d hovered", i); + } + ImGui::PopItemWidth(); - ImGui::TreePop(); + // Dummy + ImVec2 button_sz(40, 40); + ImGui::Button("A", button_sz); ImGui::SameLine(); + ImGui::Dummy(button_sz); ImGui::SameLine(); + ImGui::Button("B", button_sz); + + // Manually wrapping (we should eventually provide this as an automatic layout feature, but for now you can do it manually) + ImGui::Text("Manually wrapping:"); + ImGuiStyle& style = ImGui::GetStyle(); + int buttons_count = 20; + float window_visible_x2 = ImGui::GetWindowPos().x + ImGui::GetWindowContentRegionMax().x; + for (int n = 0; n < buttons_count; n++) + { + ImGui::PushID(n); + ImGui::Button("Box", button_sz); + float last_button_x2 = ImGui::GetItemRectMax().x; + float next_button_x2 = last_button_x2 + style.ItemSpacing.x + button_sz.x; // Expected position if next button was on same line + if (n + 1 < buttons_count && next_button_x2 < window_visible_x2) + ImGui::SameLine(); + ImGui::PopID(); } - if (ImGui::TreeNode("Groups")) + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Groups")) + { + ImGui::TextWrapped("(Using ImGui::BeginGroup()/EndGroup() to layout items. BeginGroup() basically locks the horizontal position. EndGroup() bundles the whole group so that you can use functions such as IsItemHovered() on it.)"); + ImGui::BeginGroup(); { - ImGui::TextWrapped("(Using ImGui::BeginGroup()/EndGroup() to layout items. BeginGroup() basically locks the horizontal position. EndGroup() bundles the whole group so that you can use functions such as IsItemHovered() on it.)"); ImGui::BeginGroup(); - { - ImGui::BeginGroup(); - ImGui::Button("AAA"); - ImGui::SameLine(); - ImGui::Button("BBB"); - ImGui::SameLine(); - ImGui::BeginGroup(); - ImGui::Button("CCC"); - ImGui::Button("DDD"); - ImGui::EndGroup(); - ImGui::SameLine(); - ImGui::Button("EEE"); - ImGui::EndGroup(); - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("First group hovered"); - } - // Capture the group size and create widgets using the same size - ImVec2 size = ImGui::GetItemRectSize(); - const float values[5] = { 0.5f, 0.20f, 0.80f, 0.60f, 0.25f }; - ImGui::PlotHistogram("##values", values, IM_ARRAYSIZE(values), 0, NULL, 0.0f, 1.0f, size); - - ImGui::Button("ACTION", ImVec2((size.x - ImGui::GetStyle().ItemSpacing.x)*0.5f,size.y)); + ImGui::Button("AAA"); ImGui::SameLine(); - ImGui::Button("REACTION", ImVec2((size.x - ImGui::GetStyle().ItemSpacing.x)*0.5f,size.y)); - ImGui::EndGroup(); + ImGui::Button("BBB"); ImGui::SameLine(); - - ImGui::Button("LEVERAGE\nBUZZWORD", size); + ImGui::BeginGroup(); + ImGui::Button("CCC"); + ImGui::Button("DDD"); + ImGui::EndGroup(); ImGui::SameLine(); - - if (ImGui::ListBoxHeader("List", size)) - { - ImGui::Selectable("Selected", true); - ImGui::Selectable("Not Selected", false); - ImGui::ListBoxFooter(); - } - - ImGui::TreePop(); + ImGui::Button("EEE"); + ImGui::EndGroup(); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("First group hovered"); } + // Capture the group size and create widgets using the same size + ImVec2 size = ImGui::GetItemRectSize(); + const float values[5] = { 0.5f, 0.20f, 0.80f, 0.60f, 0.25f }; + ImGui::PlotHistogram("##values", values, IM_ARRAYSIZE(values), 0, NULL, 0.0f, 1.0f, size); - if (ImGui::TreeNode("Text Baseline Alignment")) - { - ImGui::TextWrapped("(This is testing the vertical alignment that occurs on text to keep it at the same baseline as widgets. Lines only composed of text or \"small\" widgets fit in less vertical spaces than lines with normal widgets)"); - - ImGui::Text("One\nTwo\nThree"); ImGui::SameLine(); - ImGui::Text("Hello\nWorld"); ImGui::SameLine(); - ImGui::Text("Banana"); - - ImGui::Text("Banana"); ImGui::SameLine(); - ImGui::Text("Hello\nWorld"); ImGui::SameLine(); - ImGui::Text("One\nTwo\nThree"); - - ImGui::Button("HOP##1"); ImGui::SameLine(); - ImGui::Text("Banana"); ImGui::SameLine(); - ImGui::Text("Hello\nWorld"); ImGui::SameLine(); - ImGui::Text("Banana"); - - ImGui::Button("HOP##2"); ImGui::SameLine(); - ImGui::Text("Hello\nWorld"); ImGui::SameLine(); - ImGui::Text("Banana"); - - ImGui::Button("TEST##1"); ImGui::SameLine(); - ImGui::Text("TEST"); ImGui::SameLine(); - ImGui::SmallButton("TEST##2"); - - ImGui::AlignTextToFramePadding(); // If your line starts with text, call this to align it to upcoming widgets. - ImGui::Text("Text aligned to Widget"); ImGui::SameLine(); - ImGui::Button("Widget##1"); ImGui::SameLine(); - ImGui::Text("Widget"); ImGui::SameLine(); - ImGui::SmallButton("Widget##2"); ImGui::SameLine(); - ImGui::Button("Widget##3"); - - // Tree - const float spacing = ImGui::GetStyle().ItemInnerSpacing.x; - ImGui::Button("Button##1"); - ImGui::SameLine(0.0f, spacing); - if (ImGui::TreeNode("Node##1")) { for (int i = 0; i < 6; i++) ImGui::BulletText("Item %d..", i); ImGui::TreePop(); } // Dummy tree data - - ImGui::AlignTextToFramePadding(); // Vertically align text node a bit lower so it'll be vertically centered with upcoming widget. Otherwise you can use SmallButton (smaller fit). - bool node_open = ImGui::TreeNode("Node##2"); // Common mistake to avoid: if we want to SameLine after TreeNode we need to do it before we add child content. - ImGui::SameLine(0.0f, spacing); ImGui::Button("Button##2"); - if (node_open) { for (int i = 0; i < 6; i++) ImGui::BulletText("Item %d..", i); ImGui::TreePop(); } // Dummy tree data - - // Bullet - ImGui::Button("Button##3"); - ImGui::SameLine(0.0f, spacing); - ImGui::BulletText("Bullet text"); + ImGui::Button("ACTION", ImVec2((size.x - ImGui::GetStyle().ItemSpacing.x)*0.5f, size.y)); + ImGui::SameLine(); + ImGui::Button("REACTION", ImVec2((size.x - ImGui::GetStyle().ItemSpacing.x)*0.5f, size.y)); + ImGui::EndGroup(); + ImGui::SameLine(); - ImGui::AlignTextToFramePadding(); - ImGui::BulletText("Node"); - ImGui::SameLine(0.0f, spacing); ImGui::Button("Button##4"); + ImGui::Button("LEVERAGE\nBUZZWORD", size); + ImGui::SameLine(); - ImGui::TreePop(); + if (ImGui::ListBoxHeader("List", size)) + { + ImGui::Selectable("Selected", true); + ImGui::Selectable("Not Selected", false); + ImGui::ListBoxFooter(); } - if (ImGui::TreeNode("Scrolling")) - { - ImGui::TextWrapped("(Use SetScrollHere() or SetScrollFromPosY() to scroll to a given position.)"); - static bool track = true; - static int track_line = 50, scroll_to_px = 200; - ImGui::Checkbox("Track", &track); - ImGui::PushItemWidth(100); - ImGui::SameLine(130); track |= ImGui::DragInt("##line", &track_line, 0.25f, 0, 99, "Line = %d"); - bool scroll_to = ImGui::Button("Scroll To Pos"); - ImGui::SameLine(130); scroll_to |= ImGui::DragInt("##pos_y", &scroll_to_px, 1.00f, 0, 9999, "Y = %d px"); - ImGui::PopItemWidth(); - if (scroll_to) track = false; + ImGui::TreePop(); + } - for (int i = 0; i < 5; i++) - { - if (i > 0) ImGui::SameLine(); - ImGui::BeginGroup(); - ImGui::Text("%s", i == 0 ? "Top" : i == 1 ? "25%" : i == 2 ? "Center" : i == 3 ? "75%" : "Bottom"); - ImGui::BeginChild(ImGui::GetID((void*)(intptr_t)i), ImVec2(ImGui::GetWindowWidth() * 0.17f, 200.0f), true); - if (scroll_to) - ImGui::SetScrollFromPosY(ImGui::GetCursorStartPos().y + scroll_to_px, i * 0.25f); - for (int line = 0; line < 100; line++) - { - if (track && line == track_line) - { - ImGui::TextColored(ImColor(255,255,0), "Line %d", line); - ImGui::SetScrollHere(i * 0.25f); // 0.0f:top, 0.5f:center, 1.0f:bottom - } - else - { - ImGui::Text("Line %d", line); - } - } - float scroll_y = ImGui::GetScrollY(), scroll_max_y = ImGui::GetScrollMaxY(); - ImGui::EndChild(); - ImGui::Text("%.0f/%0.f", scroll_y, scroll_max_y); - ImGui::EndGroup(); - } - ImGui::TreePop(); - } + if (ImGui::TreeNode("Text Baseline Alignment")) + { + ImGui::TextWrapped("(This is testing the vertical alignment that occurs on text to keep it at the same baseline as widgets. Lines only composed of text or \"small\" widgets fit in less vertical spaces than lines with normal widgets)"); + + ImGui::Text("One\nTwo\nThree"); ImGui::SameLine(); + ImGui::Text("Hello\nWorld"); ImGui::SameLine(); + ImGui::Text("Banana"); + + ImGui::Text("Banana"); ImGui::SameLine(); + ImGui::Text("Hello\nWorld"); ImGui::SameLine(); + ImGui::Text("One\nTwo\nThree"); + + ImGui::Button("HOP##1"); ImGui::SameLine(); + ImGui::Text("Banana"); ImGui::SameLine(); + ImGui::Text("Hello\nWorld"); ImGui::SameLine(); + ImGui::Text("Banana"); + + ImGui::Button("HOP##2"); ImGui::SameLine(); + ImGui::Text("Hello\nWorld"); ImGui::SameLine(); + ImGui::Text("Banana"); + + ImGui::Button("TEST##1"); ImGui::SameLine(); + ImGui::Text("TEST"); ImGui::SameLine(); + ImGui::SmallButton("TEST##2"); + + ImGui::AlignTextToFramePadding(); // If your line starts with text, call this to align it to upcoming widgets. + ImGui::Text("Text aligned to Widget"); ImGui::SameLine(); + ImGui::Button("Widget##1"); ImGui::SameLine(); + ImGui::Text("Widget"); ImGui::SameLine(); + ImGui::SmallButton("Widget##2"); ImGui::SameLine(); + ImGui::Button("Widget##3"); + + // Tree + const float spacing = ImGui::GetStyle().ItemInnerSpacing.x; + ImGui::Button("Button##1"); + ImGui::SameLine(0.0f, spacing); + if (ImGui::TreeNode("Node##1")) { for (int i = 0; i < 6; i++) ImGui::BulletText("Item %d..", i); ImGui::TreePop(); } // Dummy tree data + + ImGui::AlignTextToFramePadding(); // Vertically align text node a bit lower so it'll be vertically centered with upcoming widget. Otherwise you can use SmallButton (smaller fit). + bool node_open = ImGui::TreeNode("Node##2"); // Common mistake to avoid: if we want to SameLine after TreeNode we need to do it before we add child content. + ImGui::SameLine(0.0f, spacing); ImGui::Button("Button##2"); + if (node_open) { for (int i = 0; i < 6; i++) ImGui::BulletText("Item %d..", i); ImGui::TreePop(); } // Dummy tree data + + // Bullet + ImGui::Button("Button##3"); + ImGui::SameLine(0.0f, spacing); + ImGui::BulletText("Bullet text"); + + ImGui::AlignTextToFramePadding(); + ImGui::BulletText("Node"); + ImGui::SameLine(0.0f, spacing); ImGui::Button("Button##4"); + + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Scrolling")) + { + ImGui::TextWrapped("(Use SetScrollHereY() or SetScrollFromPosY() to scroll to a given position.)"); + static bool track = true; + static int track_line = 50, scroll_to_px = 200; + ImGui::Checkbox("Track", &track); + ImGui::PushItemWidth(100); + ImGui::SameLine(130); track |= ImGui::DragInt("##line", &track_line, 0.25f, 0, 99, "Line = %d"); + bool scroll_to = ImGui::Button("Scroll To Pos"); + ImGui::SameLine(130); scroll_to |= ImGui::DragInt("##pos_y", &scroll_to_px, 1.00f, 0, 9999, "Y = %d px"); + ImGui::PopItemWidth(); + if (scroll_to) track = false; - if (ImGui::TreeNode("Horizontal Scrolling")) + for (int i = 0; i < 5; i++) { - ImGui::Bullet(); ImGui::TextWrapped("Horizontal scrolling for a window has to be enabled explicitly via the ImGuiWindowFlags_HorizontalScrollbar flag."); - ImGui::Bullet(); ImGui::TextWrapped("You may want to explicitly specify content width by calling SetNextWindowContentWidth() before Begin()."); - static int lines = 7; - ImGui::SliderInt("Lines", &lines, 1, 15); - ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2.0f, 1.0f)); - ImGui::BeginChild("scrolling", ImVec2(0, ImGui::GetFrameHeightWithSpacing()*7 + 30), true, ImGuiWindowFlags_HorizontalScrollbar); - for (int line = 0; line < lines; line++) + if (i > 0) ImGui::SameLine(); + ImGui::BeginGroup(); + ImGui::Text("%s", i == 0 ? "Top" : i == 1 ? "25%" : i == 2 ? "Center" : i == 3 ? "75%" : "Bottom"); + ImGui::BeginChild(ImGui::GetID((void*)(intptr_t)i), ImVec2(ImGui::GetWindowWidth() * 0.17f, 200.0f), true); + if (scroll_to) + ImGui::SetScrollFromPosY(ImGui::GetCursorStartPos().y + scroll_to_px, i * 0.25f); + for (int line = 0; line < 100; line++) { - // Display random stuff (for the sake of this trivial demo we are using basic Button+SameLine. If you want to create your own time line for a real application you may be better off - // manipulating the cursor position yourself, aka using SetCursorPos/SetCursorScreenPos to position the widgets yourself. You may also want to use the lower-level ImDrawList API) - int num_buttons = 10 + ((line & 1) ? line * 9 : line * 3); - for (int n = 0; n < num_buttons; n++) + if (track && line == track_line) { - if (n > 0) ImGui::SameLine(); - ImGui::PushID(n + line * 1000); - char num_buf[16]; - sprintf(num_buf, "%d", n); - const char* label = (!(n%15)) ? "FizzBuzz" : (!(n%3)) ? "Fizz" : (!(n%5)) ? "Buzz" : num_buf; - float hue = n*0.05f; - ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(hue, 0.6f, 0.6f)); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(hue, 0.7f, 0.7f)); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(hue, 0.8f, 0.8f)); - ImGui::Button(label, ImVec2(40.0f + sinf((float)(line + n)) * 20.0f, 0.0f)); - ImGui::PopStyleColor(3); - ImGui::PopID(); + ImGui::TextColored(ImVec4(1,1,0,1), "Line %d", line); + ImGui::SetScrollHereY(i * 0.25f); // 0.0f:top, 0.5f:center, 1.0f:bottom + } + else + { + ImGui::Text("Line %d", line); } } - float scroll_x = ImGui::GetScrollX(), scroll_max_x = ImGui::GetScrollMaxX(); + float scroll_y = ImGui::GetScrollY(), scroll_max_y = ImGui::GetScrollMaxY(); ImGui::EndChild(); - ImGui::PopStyleVar(2); - float scroll_x_delta = 0.0f; - ImGui::SmallButton("<<"); if (ImGui::IsItemActive()) scroll_x_delta = -ImGui::GetIO().DeltaTime * 1000.0f; ImGui::SameLine(); - ImGui::Text("Scroll from code"); ImGui::SameLine(); - ImGui::SmallButton(">>"); if (ImGui::IsItemActive()) scroll_x_delta = +ImGui::GetIO().DeltaTime * 1000.0f; ImGui::SameLine(); - ImGui::Text("%.0f/%.0f", scroll_x, scroll_max_x); - if (scroll_x_delta != 0.0f) - { - ImGui::BeginChild("scrolling"); // Demonstrate a trick: you can use Begin to set yourself in the context of another window (here we are already out of your child window) - ImGui::SetScrollX(ImGui::GetScrollX() + scroll_x_delta); - ImGui::End(); - } - ImGui::TreePop(); + ImGui::Text("%.0f/%0.f", scroll_y, scroll_max_y); + ImGui::EndGroup(); } + ImGui::TreePop(); + } - if (ImGui::TreeNode("Clipping")) - { - static ImVec2 size(100, 100), offset(50, 20); - ImGui::TextWrapped("On a per-widget basis we are occasionally clipping text CPU-side if it won't fit in its frame. Otherwise we are doing coarser clipping + passing a scissor rectangle to the renderer. The system is designed to try minimizing both execution and CPU/GPU rendering cost."); - ImGui::DragFloat2("size", (float*)&size, 0.5f, 0.0f, 200.0f, "%.0f"); - ImGui::TextWrapped("(Click and drag)"); - ImVec2 pos = ImGui::GetCursorScreenPos(); - ImVec4 clip_rect(pos.x, pos.y, pos.x+size.x, pos.y+size.y); - ImGui::InvisibleButton("##dummy", size); - if (ImGui::IsItemActive() && ImGui::IsMouseDragging()) { offset.x += ImGui::GetIO().MouseDelta.x; offset.y += ImGui::GetIO().MouseDelta.y; } - ImGui::GetWindowDrawList()->AddRectFilled(pos, ImVec2(pos.x+size.x,pos.y+size.y), IM_COL32(90,90,120,255)); - ImGui::GetWindowDrawList()->AddText(ImGui::GetFont(), ImGui::GetFontSize()*2.0f, ImVec2(pos.x+offset.x,pos.y+offset.y), IM_COL32(255,255,255,255), "Line 1 hello\nLine 2 clip me!", NULL, 0.0f, &clip_rect); - ImGui::TreePop(); + if (ImGui::TreeNode("Horizontal Scrolling")) + { + ImGui::Bullet(); ImGui::TextWrapped("Horizontal scrolling for a window has to be enabled explicitly via the ImGuiWindowFlags_HorizontalScrollbar flag."); + ImGui::Bullet(); ImGui::TextWrapped("You may want to explicitly specify content width by calling SetNextWindowContentWidth() before Begin()."); + static int lines = 7; + ImGui::SliderInt("Lines", &lines, 1, 15); + ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2.0f, 1.0f)); + ImGui::BeginChild("scrolling", ImVec2(0, ImGui::GetFrameHeightWithSpacing() * 7 + 30), true, ImGuiWindowFlags_HorizontalScrollbar); + for (int line = 0; line < lines; line++) + { + // Display random stuff (for the sake of this trivial demo we are using basic Button+SameLine. If you want to create your own time line for a real application you may be better off + // manipulating the cursor position yourself, aka using SetCursorPos/SetCursorScreenPos to position the widgets yourself. You may also want to use the lower-level ImDrawList API) + int num_buttons = 10 + ((line & 1) ? line * 9 : line * 3); + for (int n = 0; n < num_buttons; n++) + { + if (n > 0) ImGui::SameLine(); + ImGui::PushID(n + line * 1000); + char num_buf[16]; + sprintf(num_buf, "%d", n); + const char* label = (!(n%15)) ? "FizzBuzz" : (!(n%3)) ? "Fizz" : (!(n%5)) ? "Buzz" : num_buf; + float hue = n*0.05f; + ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(hue, 0.6f, 0.6f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(hue, 0.7f, 0.7f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(hue, 0.8f, 0.8f)); + ImGui::Button(label, ImVec2(40.0f + sinf((float)(line + n)) * 20.0f, 0.0f)); + ImGui::PopStyleColor(3); + ImGui::PopID(); + } + } + float scroll_x = ImGui::GetScrollX(), scroll_max_x = ImGui::GetScrollMaxX(); + ImGui::EndChild(); + ImGui::PopStyleVar(2); + float scroll_x_delta = 0.0f; + ImGui::SmallButton("<<"); if (ImGui::IsItemActive()) scroll_x_delta = -ImGui::GetIO().DeltaTime * 1000.0f; ImGui::SameLine(); + ImGui::Text("Scroll from code"); ImGui::SameLine(); + ImGui::SmallButton(">>"); if (ImGui::IsItemActive()) scroll_x_delta = +ImGui::GetIO().DeltaTime * 1000.0f; ImGui::SameLine(); + ImGui::Text("%.0f/%.0f", scroll_x, scroll_max_x); + if (scroll_x_delta != 0.0f) + { + ImGui::BeginChild("scrolling"); // Demonstrate a trick: you can use Begin to set yourself in the context of another window (here we are already out of your child window) + ImGui::SetScrollX(ImGui::GetScrollX() + scroll_x_delta); + ImGui::End(); } + ImGui::TreePop(); } - if (ImGui::CollapsingHeader("Popups & Modal windows")) + if (ImGui::TreeNode("Clipping")) { - if (ImGui::TreeNode("Popups")) - { - ImGui::TextWrapped("When a popup is active, it inhibits interacting with windows that are behind the popup. Clicking outside the popup closes it."); + static ImVec2 size(100, 100), offset(50, 20); + ImGui::TextWrapped("On a per-widget basis we are occasionally clipping text CPU-side if it won't fit in its frame. Otherwise we are doing coarser clipping + passing a scissor rectangle to the renderer. The system is designed to try minimizing both execution and CPU/GPU rendering cost."); + ImGui::DragFloat2("size", (float*)&size, 0.5f, 0.0f, 200.0f, "%.0f"); + ImGui::TextWrapped("(Click and drag)"); + ImVec2 pos = ImGui::GetCursorScreenPos(); + ImVec4 clip_rect(pos.x, pos.y, pos.x + size.x, pos.y + size.y); + ImGui::InvisibleButton("##dummy", size); + if (ImGui::IsItemActive() && ImGui::IsMouseDragging()) { offset.x += ImGui::GetIO().MouseDelta.x; offset.y += ImGui::GetIO().MouseDelta.y; } + ImGui::GetWindowDrawList()->AddRectFilled(pos, ImVec2(pos.x + size.x, pos.y + size.y), IM_COL32(90, 90, 120, 255)); + ImGui::GetWindowDrawList()->AddText(ImGui::GetFont(), ImGui::GetFontSize()*2.0f, ImVec2(pos.x + offset.x, pos.y + offset.y), IM_COL32(255, 255, 255, 255), "Line 1 hello\nLine 2 clip me!", NULL, 0.0f, &clip_rect); + ImGui::TreePop(); + } +} + +static void ShowDemoWindowPopups() +{ + if (!ImGui::CollapsingHeader("Popups & Modal windows")) + return; - static int selected_fish = -1; - const char* names[] = { "Bream", "Haddock", "Mackerel", "Pollock", "Tilefish" }; - static bool toggles[] = { true, false, false, false, false }; + // Popups are windows with a few special properties: + // - They block normal mouse hovering detection outside them. (*) + // - Unless modal, they can be closed by clicking anywhere outside them, or by pressing ESCAPE. + // - Their visibility state (~bool) is held internally by imgui instead of being held by the programmer as we are used to with regular Begin() calls. + // (*) One can use IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup) to bypass it and detect hovering even when normally blocked by a popup. + // Those three properties are intimately connected. The library needs to hold their visibility state because it can close popups at any time. - // Simple selection popup - // (If you want to show the current selection inside the Button itself, you may want to build a string using the "###" operator to preserve a constant ID with a variable label) - if (ImGui::Button("Select..")) - ImGui::OpenPopup("select"); - ImGui::SameLine(); - ImGui::TextUnformatted(selected_fish == -1 ? "" : names[selected_fish]); - if (ImGui::BeginPopup("select")) + // Typical use for regular windows: + // bool my_tool_is_active = false; if (ImGui::Button("Open")) my_tool_is_active = true; [...] if (my_tool_is_active) Begin("My Tool", &my_tool_is_active) { [...] } End(); + // Typical use for popups: + // if (ImGui::Button("Open")) ImGui::OpenPopup("MyPopup"); if (ImGui::BeginPopup("MyPopup") { [...] EndPopup(); } + + // With popups we have to go through a library call (here OpenPopup) to manipulate the visibility state. + // This may be a bit confusing at first but it should quickly make sense. Follow on the examples below. + + if (ImGui::TreeNode("Popups")) + { + ImGui::TextWrapped("When a popup is active, it inhibits interacting with windows that are behind the popup. Clicking outside the popup closes it."); + + static int selected_fish = -1; + const char* names[] = { "Bream", "Haddock", "Mackerel", "Pollock", "Tilefish" }; + static bool toggles[] = { true, false, false, false, false }; + + // Simple selection popup + // (If you want to show the current selection inside the Button itself, you may want to build a string using the "###" operator to preserve a constant ID with a variable label) + if (ImGui::Button("Select..")) + ImGui::OpenPopup("my_select_popup"); + ImGui::SameLine(); + ImGui::TextUnformatted(selected_fish == -1 ? "" : names[selected_fish]); + if (ImGui::BeginPopup("my_select_popup")) + { + ImGui::Text("Aquarium"); + ImGui::Separator(); + for (int i = 0; i < IM_ARRAYSIZE(names); i++) + if (ImGui::Selectable(names[i])) + selected_fish = i; + ImGui::EndPopup(); + } + + // Showing a menu with toggles + if (ImGui::Button("Toggle..")) + ImGui::OpenPopup("my_toggle_popup"); + if (ImGui::BeginPopup("my_toggle_popup")) + { + for (int i = 0; i < IM_ARRAYSIZE(names); i++) + ImGui::MenuItem(names[i], "", &toggles[i]); + if (ImGui::BeginMenu("Sub-menu")) { - ImGui::Text("Aquarium"); - ImGui::Separator(); - for (int i = 0; i < IM_ARRAYSIZE(names); i++) - if (ImGui::Selectable(names[i])) - selected_fish = i; - ImGui::EndPopup(); + ImGui::MenuItem("Click me"); + ImGui::EndMenu(); } - // Showing a menu with toggles - if (ImGui::Button("Toggle..")) - ImGui::OpenPopup("toggle"); - if (ImGui::BeginPopup("toggle")) + ImGui::Separator(); + ImGui::Text("Tooltip here"); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("I am a tooltip over a popup"); + + if (ImGui::Button("Stacked Popup")) + ImGui::OpenPopup("another popup"); + if (ImGui::BeginPopup("another popup")) { for (int i = 0; i < IM_ARRAYSIZE(names); i++) ImGui::MenuItem(names[i], "", &toggles[i]); @@ -1818,328 +1978,325 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::MenuItem("Click me"); ImGui::EndMenu(); } - - ImGui::Separator(); - ImGui::Text("Tooltip here"); - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("I am a tooltip over a popup"); - - if (ImGui::Button("Stacked Popup")) - ImGui::OpenPopup("another popup"); - if (ImGui::BeginPopup("another popup")) - { - for (int i = 0; i < IM_ARRAYSIZE(names); i++) - ImGui::MenuItem(names[i], "", &toggles[i]); - if (ImGui::BeginMenu("Sub-menu")) - { - ImGui::MenuItem("Click me"); - ImGui::EndMenu(); - } - ImGui::EndPopup(); - } ImGui::EndPopup(); } + ImGui::EndPopup(); + } - if (ImGui::Button("Popup Menu..")) - ImGui::OpenPopup("FilePopup"); - if (ImGui::BeginPopup("FilePopup")) - { - ShowExampleMenuFile(); - ImGui::EndPopup(); - } + // Call the more complete ShowExampleMenuFile which we use in various places of this demo + if (ImGui::Button("File Menu..")) + ImGui::OpenPopup("my_file_popup"); + if (ImGui::BeginPopup("my_file_popup")) + { + ShowExampleMenuFile(); + ImGui::EndPopup(); + } - ImGui::TreePop(); + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Context menus")) + { + // BeginPopupContextItem() is a helper to provide common/simple popup behavior of essentially doing: + // if (IsItemHovered() && IsMouseReleased(0)) + // OpenPopup(id); + // return BeginPopup(id); + // For more advanced uses you may want to replicate and cuztomize this code. This the comments inside BeginPopupContextItem() implementation. + static float value = 0.5f; + ImGui::Text("Value = %.3f (<-- right-click here)", value); + if (ImGui::BeginPopupContextItem("item context menu")) + { + if (ImGui::Selectable("Set to zero")) value = 0.0f; + if (ImGui::Selectable("Set to PI")) value = 3.1415f; + ImGui::PushItemWidth(-1); + ImGui::DragFloat("##Value", &value, 0.1f, 0.0f, 0.0f); + ImGui::PopItemWidth(); + ImGui::EndPopup(); } - if (ImGui::TreeNode("Context menus")) + // We can also use OpenPopupOnItemClick() which is the same as BeginPopupContextItem() but without the Begin call. + // So here we will make it that clicking on the text field with the right mouse button (1) will toggle the visibility of the popup above. + ImGui::Text("(You can also right-click me to the same popup as above.)"); + ImGui::OpenPopupOnItemClick("item context menu", 1); + + // When used after an item that has an ID (here the Button), we can skip providing an ID to BeginPopupContextItem(). + // BeginPopupContextItem() will use the last item ID as the popup ID. + // In addition here, we want to include your editable label inside the button label. We use the ### operator to override the ID (read FAQ about ID for details) + static char name[32] = "Label1"; + char buf[64]; sprintf(buf, "Button: %s###Button", name); // ### operator override ID ignoring the preceding label + ImGui::Button(buf); + if (ImGui::BeginPopupContextItem()) { - // BeginPopupContextItem() is a helper to provide common/simple popup behavior of essentially doing: - // if (IsItemHovered() && IsMouseClicked(0)) - // OpenPopup(id); - // return BeginPopup(id); - // For more advanced uses you may want to replicate and cuztomize this code. This the comments inside BeginPopupContextItem() implementation. - static float value = 0.5f; - ImGui::Text("Value = %.3f (<-- right-click here)", value); - if (ImGui::BeginPopupContextItem("item context menu")) - { - if (ImGui::Selectable("Set to zero")) value = 0.0f; - if (ImGui::Selectable("Set to PI")) value = 3.1415f; - ImGui::PushItemWidth(-1); - ImGui::DragFloat("##Value", &value, 0.1f, 0.0f, 0.0f); - ImGui::PopItemWidth(); - ImGui::EndPopup(); - } + ImGui::Text("Edit name:"); + ImGui::InputText("##edit", name, IM_ARRAYSIZE(name)); + if (ImGui::Button("Close")) + ImGui::CloseCurrentPopup(); + ImGui::EndPopup(); + } + ImGui::SameLine(); ImGui::Text("(<-- right-click here)"); - static char name[32] = "Label1"; - char buf[64]; sprintf(buf, "Button: %s###Button", name); // ### operator override ID ignoring the preceding label - ImGui::Button(buf); - if (ImGui::BeginPopupContextItem()) // When used after an item that has an ID (here the Button), we can skip providing an ID to BeginPopupContextItem(). - { - ImGui::Text("Edit name:"); - ImGui::InputText("##edit", name, IM_ARRAYSIZE(name)); - if (ImGui::Button("Close")) - ImGui::CloseCurrentPopup(); - ImGui::EndPopup(); - } - ImGui::SameLine(); ImGui::Text("(<-- right-click here)"); + ImGui::TreePop(); + } - ImGui::TreePop(); - } + if (ImGui::TreeNode("Modals")) + { + ImGui::TextWrapped("Modal windows are like popups but the user cannot close them by clicking outside the window."); - if (ImGui::TreeNode("Modals")) + if (ImGui::Button("Delete..")) + ImGui::OpenPopup("Delete?"); + if (ImGui::BeginPopupModal("Delete?", NULL, ImGuiWindowFlags_AlwaysAutoResize)) { - ImGui::TextWrapped("Modal windows are like popups but the user cannot close them by clicking outside the window."); + ImGui::Text("All those beautiful files will be deleted.\nThis operation cannot be undone!\n\n"); + ImGui::Separator(); - if (ImGui::Button("Delete..")) - ImGui::OpenPopup("Delete?"); - if (ImGui::BeginPopupModal("Delete?", NULL, ImGuiWindowFlags_AlwaysAutoResize)) - { - ImGui::Text("All those beautiful files will be deleted.\nThis operation cannot be undone!\n\n"); - ImGui::Separator(); + //static int dummy_i = 0; + //ImGui::Combo("Combo", &dummy_i, "Delete\0Delete harder\0"); - //static int dummy_i = 0; - //ImGui::Combo("Combo", &dummy_i, "Delete\0Delete harder\0"); + static bool dont_ask_me_next_time = false; + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); + ImGui::Checkbox("Don't ask me next time", &dont_ask_me_next_time); + ImGui::PopStyleVar(); - static bool dont_ask_me_next_time = false; - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0,0)); - ImGui::Checkbox("Don't ask me next time", &dont_ask_me_next_time); - ImGui::PopStyleVar(); + if (ImGui::Button("OK", ImVec2(120, 0))) { ImGui::CloseCurrentPopup(); } + ImGui::SetItemDefaultFocus(); + ImGui::SameLine(); + if (ImGui::Button("Cancel", ImVec2(120, 0))) { ImGui::CloseCurrentPopup(); } + ImGui::EndPopup(); + } - if (ImGui::Button("OK", ImVec2(120,0))) { ImGui::CloseCurrentPopup(); } - ImGui::SetItemDefaultFocus(); - ImGui::SameLine(); - if (ImGui::Button("Cancel", ImVec2(120,0))) { ImGui::CloseCurrentPopup(); } - ImGui::EndPopup(); - } + if (ImGui::Button("Stacked modals..")) + ImGui::OpenPopup("Stacked 1"); + if (ImGui::BeginPopupModal("Stacked 1")) + { + ImGui::Text("Hello from Stacked The First\nUsing style.Colors[ImGuiCol_ModalWindowDimBg] behind it."); + static int item = 1; + ImGui::Combo("Combo", &item, "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0"); + static float color[4] = { 0.4f,0.7f,0.0f,0.5f }; + ImGui::ColorEdit4("color", color); // This is to test behavior of stacked regular popups over a modal - if (ImGui::Button("Stacked modals..")) - ImGui::OpenPopup("Stacked 1"); - if (ImGui::BeginPopupModal("Stacked 1")) + if (ImGui::Button("Add another modal..")) + ImGui::OpenPopup("Stacked 2"); + if (ImGui::BeginPopupModal("Stacked 2")) { - ImGui::Text("Hello from Stacked The First\nUsing style.Colors[ImGuiCol_ModalWindowDimBg] behind it."); - static int item = 1; - ImGui::Combo("Combo", &item, "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0"); - static float color[4] = { 0.4f,0.7f,0.0f,0.5f }; - ImGui::ColorEdit4("color", color); // This is to test behavior of stacked regular popups over a modal - - if (ImGui::Button("Add another modal..")) - ImGui::OpenPopup("Stacked 2"); - if (ImGui::BeginPopupModal("Stacked 2")) - { - ImGui::Text("Hello from Stacked The Second!"); - if (ImGui::Button("Close")) - ImGui::CloseCurrentPopup(); - ImGui::EndPopup(); - } - + ImGui::Text("Hello from Stacked The Second!"); if (ImGui::Button("Close")) ImGui::CloseCurrentPopup(); ImGui::EndPopup(); } - ImGui::TreePop(); + if (ImGui::Button("Close")) + ImGui::CloseCurrentPopup(); + ImGui::EndPopup(); } - if (ImGui::TreeNode("Menus inside a regular window")) - { - ImGui::TextWrapped("Below we are testing adding menu items to a regular window. It's rather unusual but should work!"); - ImGui::Separator(); - // NB: As a quirk in this very specific example, we want to differentiate the parent of this menu from the parent of the various popup menus above. - // To do so we are encloding the items in a PushID()/PopID() block to make them two different menusets. If we don't, opening any popup above and hovering our menu here - // would open it. This is because once a menu is active, we allow to switch to a sibling menu by just hovering on it, which is the desired behavior for regular menus. - ImGui::PushID("foo"); - ImGui::MenuItem("Menu item", "CTRL+M"); - if (ImGui::BeginMenu("Menu inside a regular window")) - { - ShowExampleMenuFile(); - ImGui::EndMenu(); - } - ImGui::PopID(); - ImGui::Separator(); - ImGui::TreePop(); - } + ImGui::TreePop(); } - if (ImGui::CollapsingHeader("Columns")) + if (ImGui::TreeNode("Menus inside a regular window")) { - ImGui::PushID("Columns"); - - // Basic columns - if (ImGui::TreeNode("Basic")) + ImGui::TextWrapped("Below we are testing adding menu items to a regular window. It's rather unusual but should work!"); + ImGui::Separator(); + // NB: As a quirk in this very specific example, we want to differentiate the parent of this menu from the parent of the various popup menus above. + // To do so we are encloding the items in a PushID()/PopID() block to make them two different menusets. If we don't, opening any popup above and hovering our menu here + // would open it. This is because once a menu is active, we allow to switch to a sibling menu by just hovering on it, which is the desired behavior for regular menus. + ImGui::PushID("foo"); + ImGui::MenuItem("Menu item", "CTRL+M"); + if (ImGui::BeginMenu("Menu inside a regular window")) { - ImGui::Text("Without border:"); - ImGui::Columns(3, "mycolumns3", false); // 3-ways, no border - ImGui::Separator(); - for (int n = 0; n < 14; n++) - { - char label[32]; - sprintf(label, "Item %d", n); - if (ImGui::Selectable(label)) {} - //if (ImGui::Button(label, ImVec2(-1,0))) {} - ImGui::NextColumn(); - } - ImGui::Columns(1); - ImGui::Separator(); - - ImGui::Text("With border:"); - ImGui::Columns(4, "mycolumns"); // 4-ways, with border - ImGui::Separator(); - ImGui::Text("ID"); ImGui::NextColumn(); - ImGui::Text("Name"); ImGui::NextColumn(); - ImGui::Text("Path"); ImGui::NextColumn(); - ImGui::Text("Hovered"); ImGui::NextColumn(); - ImGui::Separator(); - const char* names[3] = { "One", "Two", "Three" }; - const char* paths[3] = { "/path/one", "/path/two", "/path/three" }; - static int selected = -1; - for (int i = 0; i < 3; i++) - { - char label[32]; - sprintf(label, "%04d", i); - if (ImGui::Selectable(label, selected == i, ImGuiSelectableFlags_SpanAllColumns)) - selected = i; - bool hovered = ImGui::IsItemHovered(); - ImGui::NextColumn(); - ImGui::Text(names[i]); ImGui::NextColumn(); - ImGui::Text(paths[i]); ImGui::NextColumn(); - ImGui::Text("%d", hovered); ImGui::NextColumn(); - } - ImGui::Columns(1); - ImGui::Separator(); - ImGui::TreePop(); + ShowExampleMenuFile(); + ImGui::EndMenu(); } + ImGui::PopID(); + ImGui::Separator(); + ImGui::TreePop(); + } +} - // Create multiple items in a same cell before switching to next column - if (ImGui::TreeNode("Mixed items")) - { - ImGui::Columns(3, "mixed"); - ImGui::Separator(); - - ImGui::Text("Hello"); - ImGui::Button("Banana"); - ImGui::NextColumn(); +static void ShowDemoWindowColumns() +{ + if (!ImGui::CollapsingHeader("Columns")) + return; - ImGui::Text("ImGui"); - ImGui::Button("Apple"); - static float foo = 1.0f; - ImGui::InputFloat("red", &foo, 0.05f, 0, "%.3f"); - ImGui::Text("An extra line here."); - ImGui::NextColumn(); + ImGui::PushID("Columns"); - ImGui::Text("Sailor"); - ImGui::Button("Corniflower"); - static float bar = 1.0f; - ImGui::InputFloat("blue", &bar, 0.05f, 0, "%.3f"); + // Basic columns + if (ImGui::TreeNode("Basic")) + { + ImGui::Text("Without border:"); + ImGui::Columns(3, "mycolumns3", false); // 3-ways, no border + ImGui::Separator(); + for (int n = 0; n < 14; n++) + { + char label[32]; + sprintf(label, "Item %d", n); + if (ImGui::Selectable(label)) {} + //if (ImGui::Button(label, ImVec2(-1,0))) {} ImGui::NextColumn(); - - if (ImGui::CollapsingHeader("Category A")) { ImGui::Text("Blah blah blah"); } ImGui::NextColumn(); - if (ImGui::CollapsingHeader("Category B")) { ImGui::Text("Blah blah blah"); } ImGui::NextColumn(); - if (ImGui::CollapsingHeader("Category C")) { ImGui::Text("Blah blah blah"); } ImGui::NextColumn(); - ImGui::Columns(1); - ImGui::Separator(); - ImGui::TreePop(); } + ImGui::Columns(1); + ImGui::Separator(); - // Word wrapping - if (ImGui::TreeNode("Word-wrapping")) - { - ImGui::Columns(2, "word-wrapping"); - ImGui::Separator(); - ImGui::TextWrapped("The quick brown fox jumps over the lazy dog."); - ImGui::TextWrapped("Hello Left"); + ImGui::Text("With border:"); + ImGui::Columns(4, "mycolumns"); // 4-ways, with border + ImGui::Separator(); + ImGui::Text("ID"); ImGui::NextColumn(); + ImGui::Text("Name"); ImGui::NextColumn(); + ImGui::Text("Path"); ImGui::NextColumn(); + ImGui::Text("Hovered"); ImGui::NextColumn(); + ImGui::Separator(); + const char* names[3] = { "One", "Two", "Three" }; + const char* paths[3] = { "/path/one", "/path/two", "/path/three" }; + static int selected = -1; + for (int i = 0; i < 3; i++) + { + char label[32]; + sprintf(label, "%04d", i); + if (ImGui::Selectable(label, selected == i, ImGuiSelectableFlags_SpanAllColumns)) + selected = i; + bool hovered = ImGui::IsItemHovered(); ImGui::NextColumn(); - ImGui::TextWrapped("The quick brown fox jumps over the lazy dog."); - ImGui::TextWrapped("Hello Right"); - ImGui::Columns(1); - ImGui::Separator(); - ImGui::TreePop(); + ImGui::Text(names[i]); ImGui::NextColumn(); + ImGui::Text(paths[i]); ImGui::NextColumn(); + ImGui::Text("%d", hovered); ImGui::NextColumn(); } + ImGui::Columns(1); + ImGui::Separator(); + ImGui::TreePop(); + } + + // Create multiple items in a same cell before switching to next column + if (ImGui::TreeNode("Mixed items")) + { + ImGui::Columns(3, "mixed"); + ImGui::Separator(); + + ImGui::Text("Hello"); + ImGui::Button("Banana"); + ImGui::NextColumn(); + + ImGui::Text("ImGui"); + ImGui::Button("Apple"); + static float foo = 1.0f; + ImGui::InputFloat("red", &foo, 0.05f, 0, "%.3f"); + ImGui::Text("An extra line here."); + ImGui::NextColumn(); + + ImGui::Text("Sailor"); + ImGui::Button("Corniflower"); + static float bar = 1.0f; + ImGui::InputFloat("blue", &bar, 0.05f, 0, "%.3f"); + ImGui::NextColumn(); + + if (ImGui::CollapsingHeader("Category A")) { ImGui::Text("Blah blah blah"); } ImGui::NextColumn(); + if (ImGui::CollapsingHeader("Category B")) { ImGui::Text("Blah blah blah"); } ImGui::NextColumn(); + if (ImGui::CollapsingHeader("Category C")) { ImGui::Text("Blah blah blah"); } ImGui::NextColumn(); + ImGui::Columns(1); + ImGui::Separator(); + ImGui::TreePop(); + } - if (ImGui::TreeNode("Borders")) + // Word wrapping + if (ImGui::TreeNode("Word-wrapping")) + { + ImGui::Columns(2, "word-wrapping"); + ImGui::Separator(); + ImGui::TextWrapped("The quick brown fox jumps over the lazy dog."); + ImGui::TextWrapped("Hello Left"); + ImGui::NextColumn(); + ImGui::TextWrapped("The quick brown fox jumps over the lazy dog."); + ImGui::TextWrapped("Hello Right"); + ImGui::Columns(1); + ImGui::Separator(); + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Borders")) + { + // NB: Future columns API should allow automatic horizontal borders. + static bool h_borders = true; + static bool v_borders = true; + ImGui::Checkbox("horizontal", &h_borders); + ImGui::SameLine(); + ImGui::Checkbox("vertical", &v_borders); + ImGui::Columns(4, NULL, v_borders); + for (int i = 0; i < 4*3; i++) { - // NB: Future columns API should allow automatic horizontal borders. - static bool h_borders = true; - static bool v_borders = true; - ImGui::Checkbox("horizontal", &h_borders); - ImGui::SameLine(); - ImGui::Checkbox("vertical", &v_borders); - ImGui::Columns(4, NULL, v_borders); - for (int i = 0; i < 4*3; i++) - { - if (h_borders && ImGui::GetColumnIndex() == 0) - ImGui::Separator(); - ImGui::Text("%c%c%c", 'a'+i, 'a'+i, 'a'+i); - ImGui::Text("Width %.2f\nOffset %.2f", ImGui::GetColumnWidth(), ImGui::GetColumnOffset()); - ImGui::NextColumn(); - } - ImGui::Columns(1); - if (h_borders) + if (h_borders && ImGui::GetColumnIndex() == 0) ImGui::Separator(); - ImGui::TreePop(); + ImGui::Text("%c%c%c", 'a'+i, 'a'+i, 'a'+i); + ImGui::Text("Width %.2f\nOffset %.2f", ImGui::GetColumnWidth(), ImGui::GetColumnOffset()); + ImGui::NextColumn(); } - - // Scrolling columns - /* - if (ImGui::TreeNode("Vertical Scrolling")) - { - ImGui::BeginChild("##header", ImVec2(0, ImGui::GetTextLineHeightWithSpacing()+ImGui::GetStyle().ItemSpacing.y)); - ImGui::Columns(3); - ImGui::Text("ID"); ImGui::NextColumn(); - ImGui::Text("Name"); ImGui::NextColumn(); - ImGui::Text("Path"); ImGui::NextColumn(); - ImGui::Columns(1); + ImGui::Columns(1); + if (h_borders) ImGui::Separator(); - ImGui::EndChild(); - ImGui::BeginChild("##scrollingregion", ImVec2(0, 60)); - ImGui::Columns(3); - for (int i = 0; i < 10; i++) - { - ImGui::Text("%04d", i); ImGui::NextColumn(); - ImGui::Text("Foobar"); ImGui::NextColumn(); - ImGui::Text("/path/foobar/%04d/", i); ImGui::NextColumn(); - } - ImGui::Columns(1); - ImGui::EndChild(); - ImGui::TreePop(); - } - */ + ImGui::TreePop(); + } - if (ImGui::TreeNode("Horizontal Scrolling")) + // Scrolling columns + /* + if (ImGui::TreeNode("Vertical Scrolling")) + { + ImGui::BeginChild("##header", ImVec2(0, ImGui::GetTextLineHeightWithSpacing()+ImGui::GetStyle().ItemSpacing.y)); + ImGui::Columns(3); + ImGui::Text("ID"); ImGui::NextColumn(); + ImGui::Text("Name"); ImGui::NextColumn(); + ImGui::Text("Path"); ImGui::NextColumn(); + ImGui::Columns(1); + ImGui::Separator(); + ImGui::EndChild(); + ImGui::BeginChild("##scrollingregion", ImVec2(0, 60)); + ImGui::Columns(3); + for (int i = 0; i < 10; i++) { - ImGui::SetNextWindowContentSize(ImVec2(1500.0f, 0.0f)); - ImGui::BeginChild("##ScrollingRegion", ImVec2(0, ImGui::GetFontSize() * 20), false, ImGuiWindowFlags_HorizontalScrollbar); - ImGui::Columns(10); - int ITEMS_COUNT = 2000; - ImGuiListClipper clipper(ITEMS_COUNT); // Also demonstrate using the clipper for large list - while (clipper.Step()) - { - for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) - for (int j = 0; j < 10; j++) - { - ImGui::Text("Line %d Column %d...", i, j); - ImGui::NextColumn(); - } - } - ImGui::Columns(1); - ImGui::EndChild(); - ImGui::TreePop(); + ImGui::Text("%04d", i); ImGui::NextColumn(); + ImGui::Text("Foobar"); ImGui::NextColumn(); + ImGui::Text("/path/foobar/%04d/", i); ImGui::NextColumn(); } + ImGui::Columns(1); + ImGui::EndChild(); + ImGui::TreePop(); + } + */ - bool node_open = ImGui::TreeNode("Tree within single cell"); - ImGui::SameLine(); ShowHelpMarker("NB: Tree node must be poped before ending the cell. There's no storage of state per-cell."); - if (node_open) - { - ImGui::Columns(2, "tree items"); - ImGui::Separator(); - if (ImGui::TreeNode("Hello")) { ImGui::BulletText("Sailor"); ImGui::TreePop(); } ImGui::NextColumn(); - if (ImGui::TreeNode("Bonjour")) { ImGui::BulletText("Marin"); ImGui::TreePop(); } ImGui::NextColumn(); - ImGui::Columns(1); - ImGui::Separator(); - ImGui::TreePop(); + if (ImGui::TreeNode("Horizontal Scrolling")) + { + ImGui::SetNextWindowContentSize(ImVec2(1500.0f, 0.0f)); + ImGui::BeginChild("##ScrollingRegion", ImVec2(0, ImGui::GetFontSize() * 20), false, ImGuiWindowFlags_HorizontalScrollbar); + ImGui::Columns(10); + int ITEMS_COUNT = 2000; + ImGuiListClipper clipper(ITEMS_COUNT); // Also demonstrate using the clipper for large list + while (clipper.Step()) + { + for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) + for (int j = 0; j < 10; j++) + { + ImGui::Text("Line %d Column %d...", i, j); + ImGui::NextColumn(); + } } - ImGui::PopID(); + ImGui::Columns(1); + ImGui::EndChild(); + ImGui::TreePop(); } + bool node_open = ImGui::TreeNode("Tree within single cell"); + ImGui::SameLine(); ShowHelpMarker("NB: Tree node must be poped before ending the cell. There's no storage of state per-cell."); + if (node_open) + { + ImGui::Columns(2, "tree items"); + ImGui::Separator(); + if (ImGui::TreeNode("Hello")) { ImGui::BulletText("Sailor"); ImGui::TreePop(); } ImGui::NextColumn(); + if (ImGui::TreeNode("Bonjour")) { ImGui::BulletText("Marin"); ImGui::TreePop(); } ImGui::NextColumn(); + ImGui::Columns(1); + ImGui::Separator(); + ImGui::TreePop(); + } + ImGui::PopID(); +} + +static void ShowDemoWindowMisc() +{ if (ImGui::CollapsingHeader("Filtering")) { static ImGuiTextFilter filter; @@ -2165,20 +2322,6 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::Text("WantSetMousePos: %d", io.WantSetMousePos); ImGui::Text("NavActive: %d, NavVisible: %d", io.NavActive, io.NavVisible); - ImGui::Checkbox("io.MouseDrawCursor", &io.MouseDrawCursor); - ImGui::SameLine(); ShowHelpMarker("Instruct ImGui to render a mouse cursor for you in software. Note that a mouse cursor rendered via your application GPU rendering path will feel more laggy than hardware cursor, but will be more in sync with your other visuals.\n\nSome desktop applications may use both kinds of cursors (e.g. enable software cursor only when resizing/dragging something)."); - - ImGui::CheckboxFlags("io.ConfigFlags: NavEnableGamepad [beta]", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NavEnableGamepad); - ImGui::CheckboxFlags("io.ConfigFlags: NavEnableKeyboard [beta]", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NavEnableKeyboard); - ImGui::CheckboxFlags("io.ConfigFlags: NavEnableSetMousePos", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NavEnableSetMousePos); - ImGui::SameLine(); ShowHelpMarker("Instruct navigation to move the mouse cursor. See comment for ImGuiConfigFlags_NavEnableSetMousePos."); - ImGui::CheckboxFlags("io.ConfigFlags: NoMouseCursorChange", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NoMouseCursorChange); - ImGui::SameLine(); ShowHelpMarker("Instruct back-end to not alter mouse cursor shape and visibility."); - ImGui::Checkbox("io.ConfigCursorBlink", &io.ConfigCursorBlink); - ImGui::SameLine(); ShowHelpMarker("Set to false to disable blinking cursor, for users who consider it distracting"); - ImGui::Checkbox("io.ConfigResizeWindowsFromEdges [beta]", &io.ConfigResizeWindowsFromEdges); - ImGui::SameLine(); ShowHelpMarker("Enable resizing of windows from their edges and from the lower-left corner. This requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback."); - if (ImGui::TreeNode("Keyboard, Mouse & Navigation State")) { if (ImGui::IsMousePosValid()) @@ -2311,11 +2454,12 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::TreePop(); } } - - // End of ShowDemoWindow() - ImGui::End(); } +//----------------------------------------------------------------------------- +// [SECTION] Style Editor / ShowStyleEditor() +//----------------------------------------------------------------------------- + // Demo helper function to select among default colors. See ShowStyleEditor() for more advanced options. // Here we use the simplified Combo() api that packs items into a single literal string. Useful for quick combo boxes where the choices are known locally. bool ImGui::ShowStyleSelector(const char* label) @@ -2398,8 +2542,8 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ImGui::Checkbox("Anti-aliased lines", &style.AntiAliasedLines); ImGui::SameLine(); ShowHelpMarker("When disabling anti-aliasing lines, you'll probably want to disable borders in your style as well."); ImGui::Checkbox("Anti-aliased fill", &style.AntiAliasedFill); ImGui::PushItemWidth(100); - ImGui::DragFloat("Curve Tessellation Tolerance", &style.CurveTessellationTol, 0.02f, 0.10f, FLT_MAX, NULL, 2.0f); - if (style.CurveTessellationTol < 0.0f) style.CurveTessellationTol = 0.10f; + ImGui::DragFloat("Curve Tessellation Tolerance", &style.CurveTessellationTol, 0.02f, 0.10f, FLT_MAX, "%.2f", 2.0f); + if (style.CurveTessellationTol < 0.10f) style.CurveTessellationTol = 0.10f; ImGui::DragFloat("Global Alpha", &style.Alpha, 0.005f, 0.20f, 1.0f, "%.2f"); // Not exposing zero here so user doesn't "lose" the UI (zero alpha clips all widgets). But application code could have a toggle to switch between zero and non-zero. ImGui::PopItemWidth(); ImGui::TreePop(); @@ -2579,7 +2723,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) } //----------------------------------------------------------------------------- -// EXAMPLE APP CODE: MAIN MENU BAR +// [SECTION] Example App: Main Menu Bar / ShowExampleAppMainMenuBar() //----------------------------------------------------------------------------- // Demonstrate creating a fullscreen menu bar and populating it. @@ -2672,10 +2816,10 @@ static void ShowExampleMenuFile() } //----------------------------------------------------------------------------- -// EXAMPLE APP CODE: CONSOLE +// [SECTION] Example App: Debug Console / ShowExampleAppConsole() //----------------------------------------------------------------------------- -// Demonstrating creating a simple console window, with scrolling, filtering, completion and history. +// Demonstrate creating a simple console window, with scrolling, filtering, completion and history. // For the console example, here we are using a more C++ like approach of declaring a class to hold the data and the functions. struct ExampleAppConsole { @@ -2807,7 +2951,7 @@ struct ExampleAppConsole if (copy_to_clipboard) ImGui::LogFinish(); if (ScrollToBottom) - ImGui::SetScrollHere(1.0f); + ImGui::SetScrollHereY(1.0f); ScrollToBottom = false; ImGui::PopStyleVar(); ImGui::EndChild(); @@ -2985,7 +3129,7 @@ static void ShowExampleAppConsole(bool* p_open) } //----------------------------------------------------------------------------- -// EXAMPLE APP CODE: LOG +// [SECTION] Example App: Debug Log / ShowExampleAppLog() //----------------------------------------------------------------------------- // Usage: @@ -3049,7 +3193,7 @@ struct ExampleAppLog } if (ScrollToBottom) - ImGui::SetScrollHere(1.0f); + ImGui::SetScrollHereY(1.0f); ScrollToBottom = false; ImGui::EndChild(); ImGui::End(); @@ -3075,7 +3219,7 @@ static void ShowExampleAppLog(bool* p_open) } //----------------------------------------------------------------------------- -// EXAMPLE APP CODE: SIMPLE LAYOUT +// [SECTION] Example App: Simple Layout / ShowExampleAppLayout() //----------------------------------------------------------------------------- // Demonstrate create a window with multiple child windows. @@ -3123,7 +3267,7 @@ static void ShowExampleAppLayout(bool* p_open) } //----------------------------------------------------------------------------- -// EXAMPLE APP CODE: PROPERTY EDITOR +// [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor() //----------------------------------------------------------------------------- // Demonstrate create a simple property editor. @@ -3196,7 +3340,7 @@ static void ShowExampleAppPropertyEditor(bool* p_open) } //----------------------------------------------------------------------------- -// EXAMPLE APP CODE: LONG TEXT +// [SECTION] Example App: Long Text / ShowExampleAppLongText() //----------------------------------------------------------------------------- // Demonstrate/test rendering huge amount of text, and the incidence of clipping. @@ -3254,7 +3398,7 @@ static void ShowExampleAppLongText(bool* p_open) } //----------------------------------------------------------------------------- -// EXAMPLE APP CODE: AUTO RESIZE +// [SECTION] Example App: Auto Resize / ShowExampleAppAutoResize() //----------------------------------------------------------------------------- // Demonstrate creating a window which gets auto-resized according to its content. @@ -3275,7 +3419,7 @@ static void ShowExampleAppAutoResize(bool* p_open) } //----------------------------------------------------------------------------- -// EXAMPLE APP CODE: CONSTRAINED RESIZE +// [SECTION] Example App: Constrained Resize / ShowExampleAppConstrainedResize() //----------------------------------------------------------------------------- // Demonstrate creating a window with custom resize constraints. @@ -3326,7 +3470,7 @@ static void ShowExampleAppConstrainedResize(bool* p_open) } //----------------------------------------------------------------------------- -// EXAMPLE APP CODE: SIMPLE OVERLAY +// [SECTION] Example App: Simple Overlay / ShowExampleAppSimpleOverlay() //----------------------------------------------------------------------------- // Demonstrate creating a simple static window with no decoration + a context-menu to choose which corner of the screen to use. @@ -3362,7 +3506,7 @@ static void ShowExampleAppSimpleOverlay(bool* p_open) } //----------------------------------------------------------------------------- -// EXAMPLE APP CODE: WINDOW TITLES +// [SECTION] Example App: Manipulating Window Titles / ShowExampleAppWindowTitles() //----------------------------------------------------------------------------- // Demonstrate using "##" and "###" in identifiers to manipulate ID generation. @@ -3393,7 +3537,7 @@ static void ShowExampleAppWindowTitles(bool*) } //----------------------------------------------------------------------------- -// EXAMPLE APP CODE: CUSTOM RENDERING +// [SECTION] Example App: Custom Rendering using ImDrawList API / ShowExampleAppCustomRendering() //----------------------------------------------------------------------------- // Demonstrate using the low-level ImDrawList to draw custom shapes. diff --git a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_draw.cpp b/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_draw.cpp index 1e583cf1..7c8da648 100644 --- a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_draw.cpp +++ b/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_draw.cpp @@ -1,19 +1,22 @@ -// dear imgui, v1.64 +// dear imgui, v1.66 // (drawing and font code) /* Index of this file: -- Cruft for stb_truetype/stb_rectpack implementation -- Style functions (default style) -- ImDrawList -- ImDrawData -- ShadeVertsXXX helpers functions -- ImFontConfig -- ImFontAtlas -- ImFont -- Internal Render Helpers -- Default font data + +// [SECTION] STB libraries implementation +// [SECTION] Style functions +// [SECTION] ImDrawList +// [SECTION] ImDrawData +// [SECTION] Helpers ShadeVertsXXX functions +// [SECTION] ImFontConfig +// [SECTION] ImFontAtlas +// [SECTION] ImFontAtlas glyph ranges helpers + GlyphRangesBuilder +// [SECTION] ImFont +// [SECTION] Internal Render Helpers +// [SECTION] Decompression code +// [SECTION] Default font data (ProggyClean.ttf) */ @@ -73,7 +76,7 @@ Index of this file: #endif //------------------------------------------------------------------------- -// STB libraries implementation +// [SECTION] STB libraries implementation //------------------------------------------------------------------------- // Compile time options: @@ -117,7 +120,7 @@ namespace IMGUI_STB_NAMESPACE #ifdef IMGUI_STB_RECT_PACK_FILENAME #include IMGUI_STB_RECT_PACK_FILENAME #else -#include "stb_rect_pack.h" +#include "imstb_rectpack.h" #endif #endif @@ -140,7 +143,7 @@ namespace IMGUI_STB_NAMESPACE #ifdef IMGUI_STB_TRUETYPE_FILENAME #include IMGUI_STB_TRUETYPE_FILENAME #else -#include "stb_truetype.h" +#include "imstb_truetype.h" #endif #endif @@ -162,7 +165,7 @@ using namespace IMGUI_STB_NAMESPACE; #endif //----------------------------------------------------------------------------- -// Style functions +// [SECTION] Style functions //----------------------------------------------------------------------------- void ImGui::StyleColorsDark(ImGuiStyle* dst) @@ -317,7 +320,7 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst) } //----------------------------------------------------------------------------- -// ImDrawListData +// ImDrawList //----------------------------------------------------------------------------- ImDrawListSharedData::ImDrawListSharedData() @@ -335,10 +338,6 @@ ImDrawListSharedData::ImDrawListSharedData() } } -//----------------------------------------------------------------------------- -// ImDrawList -//----------------------------------------------------------------------------- - void ImDrawList::Clear() { CmdBuffer.resize(0); @@ -824,6 +823,9 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 void ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_count, ImU32 col) { + if (points_count < 3) + return; + const ImVec2 uv = _Data->TexUvWhitePixel; if (Flags & ImDrawListFlags_AntiAliasedFill) @@ -1228,7 +1230,7 @@ void ImDrawList::AddImageRounded(ImTextureID user_texture_id, const ImVec2& a, c } //----------------------------------------------------------------------------- -// ImDrawData +// [SECTION] ImDrawData //----------------------------------------------------------------------------- // For backward compatibility: convert all buffers from indexed to de-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering! @@ -1265,7 +1267,7 @@ void ImDrawData::ScaleClipRects(const ImVec2& scale) } //----------------------------------------------------------------------------- -// Shade functions +// [SECTION] Helpers ShadeVertsXXX functions //----------------------------------------------------------------------------- // Generic linear color gradient, write to RGB fields, leave A untouched. @@ -1312,7 +1314,7 @@ void ImGui::ShadeVertsLinearUV(ImDrawList* draw_list, int vert_start_idx, int ve } //----------------------------------------------------------------------------- -// ImFontConfig +// [SECTION] ImFontConfig //----------------------------------------------------------------------------- ImFontConfig::ImFontConfig() @@ -1338,7 +1340,7 @@ ImFontConfig::ImFontConfig() } //----------------------------------------------------------------------------- -// ImFontAtlas +// [SECTION] ImFontAtlas //----------------------------------------------------------------------------- // A work of art lies ahead! (. = white layer, X = black layer, others are blank) @@ -1394,7 +1396,7 @@ ImFontAtlas::ImFontAtlas() { Locked = false; Flags = ImFontAtlasFlags_None; - TexID = NULL; + TexID = (ImTextureID)NULL; TexDesiredWidth = 0; TexGlyphPadding = 1; @@ -1710,7 +1712,7 @@ bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) ImFontAtlasBuildRegisterDefaultCustomRects(atlas); - atlas->TexID = NULL; + atlas->TexID = (ImTextureID)NULL; atlas->TexWidth = atlas->TexHeight = 0; atlas->TexUvScale = ImVec2(0.0f, 0.0f); atlas->TexUvWhitePixel = ImVec2(0.0f, 0.0f); @@ -1735,11 +1737,11 @@ bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) // Start packing const int max_tex_height = 1024*32; - + // [Bruno Levy]: replaced "={}" with memset() (={} generates a warning on MSVC). stbtt_pack_context spc; memset(&spc, 0, sizeof(spc)); - + if (!stbtt_PackBegin(&spc, NULL, atlas->TexWidth, max_tex_height, 0, atlas->TexGlyphPadding, NULL)) return false; stbtt_PackSetOversampling(&spc, 1, 1); @@ -1900,7 +1902,7 @@ bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) continue; const int codepoint = range.first_unicode_codepoint_in_range + char_idx; - if (cfg.MergeMode && dst_font->FindGlyphNoFallback((unsigned short)codepoint)) + if (cfg.MergeMode && dst_font->FindGlyphNoFallback((ImWchar)codepoint)) continue; float char_advance_x_org = pc.xadvance; @@ -2079,6 +2081,10 @@ static void UnpackAccumulativeOffsetsIntoRanges(int base_codepoint, const short* out_ranges[0] = 0; } +//------------------------------------------------------------------------- +// [SECTION] ImFontAtlas glyph ranges helpers + GlyphRangesBuilder +//------------------------------------------------------------------------- + const ImWchar* ImFontAtlas::GetGlyphRangesChineseSimplifiedCommon() { // Store 2500 regularly used characters for Simplified Chinese. @@ -2228,10 +2234,6 @@ const ImWchar* ImFontAtlas::GetGlyphRangesThai() return &ranges[0]; } -//----------------------------------------------------------------------------- -// ImFontAtlas::GlyphRangesBuilder -//----------------------------------------------------------------------------- - void ImFontAtlas::GlyphRangesBuilder::AddText(const char* text, const char* text_end) { while (text_end ? (text < text_end) : *text) @@ -2267,7 +2269,7 @@ void ImFontAtlas::GlyphRangesBuilder::BuildRanges(ImVector* out_ranges) } //----------------------------------------------------------------------------- -// ImFont +// [SECTION] ImFont //----------------------------------------------------------------------------- ImFont::ImFont() @@ -2322,21 +2324,21 @@ void ImFont::BuildLookupTable() { int codepoint = (int)Glyphs[i].Codepoint; IndexAdvanceX[codepoint] = Glyphs[i].AdvanceX; - IndexLookup[codepoint] = (unsigned short)i; + IndexLookup[codepoint] = (ImWchar)i; } // Create a glyph to handle TAB // FIXME: Needs proper TAB handling but it needs to be contextualized (or we could arbitrary say that each string starts at "column 0" ?) - if (FindGlyph((unsigned short)' ')) + if (FindGlyph((ImWchar)' ')) { if (Glyphs.back().Codepoint != '\t') // So we can call this function multiple times Glyphs.resize(Glyphs.Size + 1); ImFontGlyph& tab_glyph = Glyphs.back(); - tab_glyph = *FindGlyph((unsigned short)' '); + tab_glyph = *FindGlyph((ImWchar)' '); tab_glyph.Codepoint = '\t'; tab_glyph.AdvanceX *= 4; IndexAdvanceX[(int)tab_glyph.Codepoint] = (float)tab_glyph.AdvanceX; - IndexLookup[(int)tab_glyph.Codepoint] = (unsigned short)(Glyphs.Size-1); + IndexLookup[(int)tab_glyph.Codepoint] = (ImWchar)(Glyphs.Size-1); } FallbackGlyph = FindGlyphNoFallback(FallbackChar); @@ -2358,7 +2360,7 @@ void ImFont::GrowIndex(int new_size) if (new_size <= IndexLookup.Size) return; IndexAdvanceX.resize(new_size, -1.0f); - IndexLookup.resize(new_size, (unsigned short)-1); + IndexLookup.resize(new_size, (ImWchar)-1); } // x0/y0/x1/y1 are offset from the character upper-left layout position, in pixels. Therefore x0/y0 are often fairly close to zero. @@ -2391,13 +2393,13 @@ void ImFont::AddRemapChar(ImWchar dst, ImWchar src, bool overwrite_dst) IM_ASSERT(IndexLookup.Size > 0); // Currently this can only be called AFTER the font has been built, aka after calling ImFontAtlas::GetTexDataAs*() function. int index_size = IndexLookup.Size; - if (dst < index_size && IndexLookup.Data[dst] == (unsigned short)-1 && !overwrite_dst) // 'dst' already exists + if (dst < index_size && IndexLookup.Data[dst] == (ImWchar)-1 && !overwrite_dst) // 'dst' already exists return; if (src >= index_size && dst >= index_size) // both 'dst' and 'src' don't exist -> no-op return; GrowIndex(dst + 1); - IndexLookup[dst] = (src < index_size) ? IndexLookup.Data[src] : (unsigned short)-1; + IndexLookup[dst] = (src < index_size) ? IndexLookup.Data[src] : (ImWchar)-1; IndexAdvanceX[dst] = (src < index_size) ? IndexAdvanceX.Data[src] : 1.0f; } @@ -2405,8 +2407,8 @@ const ImFontGlyph* ImFont::FindGlyph(ImWchar c) const { if (c >= IndexLookup.Size) return FallbackGlyph; - const unsigned short i = IndexLookup[c]; - if (i == (unsigned short)-1) + const ImWchar i = IndexLookup[c]; + if (i == (ImWchar)-1) return FallbackGlyph; return &Glyphs.Data[i]; } @@ -2415,8 +2417,8 @@ const ImFontGlyph* ImFont::FindGlyphNoFallback(ImWchar c) const { if (c >= IndexLookup.Size) return NULL; - const unsigned short i = IndexLookup[c]; - if (i == (unsigned short)-1) + const ImWchar i = IndexLookup[c]; + if (i == (ImWchar)-1) return NULL; return &Glyphs.Data[i]; } @@ -2614,7 +2616,7 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons return text_size; } -void ImFont::RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, unsigned short c) const +void ImFont::RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, ImWchar c) const { if (c == ' ' || c == '\t' || c == '\n' || c == '\r') // Match behavior of RenderText(), those 4 codepoints are hard-coded. return; @@ -2649,11 +2651,10 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col // Fast-forward to first visible line const char* s = text_begin; if (y + line_height < clip_rect.y && !word_wrap_enabled) - while (y + line_height < clip_rect.y) + while (y + line_height < clip_rect.y && s < text_end) { - while (s < text_end) - if (*s++ == '\n') - break; + s = (const char*)memchr(s, '\n', text_end - s); + s = s ? s + 1 : text_end; y += line_height; } @@ -2663,15 +2664,16 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col { const char* s_end = s; float y_end = y; - while (y_end < clip_rect.w) + while (y_end < clip_rect.w && s_end < text_end) { - while (s_end < text_end) - if (*s_end++ == '\n') - break; + s_end = (const char*)memchr(s_end, '\n', text_end - s_end); + s = s ? s + 1 : text_end; y_end += line_height; } text_end = s_end; } + if (s == text_end) + return; // Reserve vertices for remaining worse case (over-reserving is useful and easily amortized) const int vtx_count_max = (int)(text_end - s) * 4; @@ -2739,7 +2741,7 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col } float char_width = 0.0f; - if (const ImFontGlyph* glyph = FindGlyph((unsigned short)c)) + if (const ImFontGlyph* glyph = FindGlyph((ImWchar)c)) { char_width = glyph->AdvanceX * scale; @@ -2818,12 +2820,12 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col } //----------------------------------------------------------------------------- -// Internals Render Helpers +// [SECTION] Internal Render Helpers // (progressively moved from imgui.cpp to here when they are redesigned to stop accessing ImGui global state) //----------------------------------------------------------------------------- -// RenderMouseCursor() -// RenderArrowPointingAt() -// RenderRectFilledRangeH() +// - RenderMouseCursor() +// - RenderArrowPointingAt() +// - RenderRectFilledRangeH() //----------------------------------------------------------------------------- void ImGui::RenderMouseCursor(ImDrawList* draw_list, ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor) @@ -2932,8 +2934,9 @@ void ImGui::RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, Im draw_list->PathFillConvex(col); } + //----------------------------------------------------------------------------- -// DEFAULT FONT DATA +// [SECTION] Decompression code //----------------------------------------------------------------------------- // Compressed with stb_compress() then converted to a C array and encoded as base85. // Use the program in misc/fonts/binary_to_compressed_c.cpp to create the array from a TTF file. @@ -3052,6 +3055,8 @@ static unsigned int stb_decompress(unsigned char *output, const unsigned char *i } } +//----------------------------------------------------------------------------- +// [SECTION] Default font data (ProggyClean.ttf) //----------------------------------------------------------------------------- // ProggyClean.ttf // Copyright (c) 2004, 2005 Tristan Grimmer diff --git a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_glfw.cpp b/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_glfw.cpp index bc4331d9..4385bbe9 100644 --- a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_glfw.cpp +++ b/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_glfw.cpp @@ -1,10 +1,10 @@ -// ImGui Platform Binding for: GLFW +// dear imgui: Platform Binding for GLFW // This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan..) // (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) // Implemented features: // [X] Platform: Clipboard support. -// [X] Platform: Gamepad navigation mapping. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. +// [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // [x] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: 3 cursors types are missing from GLFW. // [X] Platform: Keyboard arrays indexed using GLFW_KEY_* codes, e.g. ImGui::IsKeyPressed(GLFW_KEY_SPACE). @@ -14,6 +14,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2018-11-07: Inputs: When installing our GLFW callbacks, we save user's previously installed ones - if any - and chain call them. // 2018-08-01: Inputs: Workaround for Emscripten which doesn't seem to handle focus related calls. // 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor. // 2018-06-08: Misc: Extracted imgui_impl_glfw.cpp/.h away from the old combined GLFW+OpenGL/Vulkan examples. @@ -58,6 +59,7 @@ #endif #endif + #define GLFW_HAS_WINDOW_TOPMOST (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ GLFW_FLOATING #define GLFW_HAS_WINDOW_HOVERED (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ GLFW_HOVERED #define GLFW_HAS_WINDOW_ALPHA (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwSetWindowOpacity @@ -71,11 +73,17 @@ enum GlfwClientApi GlfwClientApi_OpenGL, GlfwClientApi_Vulkan }; -static GLFWwindow* g_Window = NULL; -static GlfwClientApi g_ClientApi = GlfwClientApi_Unknown; -static double g_Time = 0.0; -static bool g_MouseJustPressed[5] = { false, false, false, false, false }; -static GLFWcursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = { 0 }; +static GLFWwindow* g_Window = NULL; +static GlfwClientApi g_ClientApi = GlfwClientApi_Unknown; +static double g_Time = 0.0; +static bool g_MouseJustPressed[5] = { false, false, false, false, false }; +static GLFWcursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = { 0 }; + +// Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. +static GLFWmousebuttonfun g_PrevUserCallbackMousebutton = NULL; +static GLFWscrollfun g_PrevUserCallbackScroll = NULL; +static GLFWkeyfun g_PrevUserCallbackKey = NULL; +static GLFWcharfun g_PrevUserCallbackChar = NULL; // [Bruno Levy] 01/06/2017 Do not use GLFW3 clipboard under emscripten, use built-in // ImGUI clipboard instead. @@ -93,13 +101,16 @@ static void ImGui_ImplGlfw_SetClipboardText(void* user_data, const char* text) #endif -void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow*, int button, int action, int /*mods*/) +void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods) { + if (g_PrevUserCallbackMousebutton != NULL) + g_PrevUserCallbackMousebutton(window, button, action, mods); + if (action == GLFW_PRESS && button >= 0 && button < IM_ARRAYSIZE(g_MouseJustPressed)) g_MouseJustPressed[button] = true; } -void ImGui_ImplGlfw_ScrollCallback(GLFWwindow*, double xoffset, double yoffset) +void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset) { // [Bruno Levy] 01/06/2017 // Under emscripten and apple, mouse wheel is inversed @@ -107,41 +118,43 @@ void ImGui_ImplGlfw_ScrollCallback(GLFWwindow*, double xoffset, double yoffset) #if defined(__EMSCRIPTEN__) || defined(__APPLE__) yoffset *= -1.0; #endif + + if (g_PrevUserCallbackScroll != NULL) + g_PrevUserCallbackScroll(window, xoffset, yoffset); + ImGuiIO& io = ImGui::GetIO(); io.MouseWheelH += (float)xoffset; io.MouseWheel += (float)yoffset; } -void ImGui_ImplGlfw_KeyCallback(GLFWwindow*, int key, int, int action, int mods) +void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) { + if (g_PrevUserCallbackKey != NULL) + g_PrevUserCallbackKey(window, key, scancode, action, mods); + ImGuiIO& io = ImGui::GetIO(); if (action == GLFW_PRESS) io.KeysDown[key] = true; if (action == GLFW_RELEASE) io.KeysDown[key] = false; - (void)mods; // Modifiers are not reliable across systems + // Modifiers are not reliable across systems io.KeyCtrl = io.KeysDown[GLFW_KEY_LEFT_CONTROL] || io.KeysDown[GLFW_KEY_RIGHT_CONTROL]; io.KeyShift = io.KeysDown[GLFW_KEY_LEFT_SHIFT] || io.KeysDown[GLFW_KEY_RIGHT_SHIFT]; io.KeyAlt = io.KeysDown[GLFW_KEY_LEFT_ALT] || io.KeysDown[GLFW_KEY_RIGHT_ALT]; io.KeySuper = io.KeysDown[GLFW_KEY_LEFT_SUPER] || io.KeysDown[GLFW_KEY_RIGHT_SUPER]; } -void ImGui_ImplGlfw_CharCallback(GLFWwindow*, unsigned int c) +void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c) { + if (g_PrevUserCallbackChar != NULL) + g_PrevUserCallbackChar(window, c); + ImGuiIO& io = ImGui::GetIO(); if (c > 0 && c < 0x10000) io.AddInputCharacter((unsigned short)c); } -void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window) -{ - glfwSetMouseButtonCallback(window, ImGui_ImplGlfw_MouseButtonCallback); - glfwSetScrollCallback(window, ImGui_ImplGlfw_ScrollCallback); - glfwSetKeyCallback(window, ImGui_ImplGlfw_KeyCallback); - glfwSetCharCallback(window, ImGui_ImplGlfw_CharCallback); -} - static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, GlfwClientApi client_api) { g_Window = window; @@ -196,8 +209,18 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw g_MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); // FIXME: GLFW doesn't have this. g_MouseCursors[ImGuiMouseCursor_Hand] = glfwCreateStandardCursor(GLFW_HAND_CURSOR); + // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. + g_PrevUserCallbackMousebutton = NULL; + g_PrevUserCallbackScroll = NULL; + g_PrevUserCallbackKey = NULL; + g_PrevUserCallbackChar = NULL; if (install_callbacks) - ImGui_ImplGlfw_InstallCallbacks(window); + { + g_PrevUserCallbackMousebutton = glfwSetMouseButtonCallback(window, ImGui_ImplGlfw_MouseButtonCallback); + g_PrevUserCallbackScroll = glfwSetScrollCallback(window, ImGui_ImplGlfw_ScrollCallback); + g_PrevUserCallbackKey = glfwSetKeyCallback(window, ImGui_ImplGlfw_KeyCallback); + g_PrevUserCallbackChar = glfwSetCharCallback(window, ImGui_ImplGlfw_CharCallback); + } g_ClientApi = client_api; return true; @@ -237,10 +260,12 @@ static void ImGui_ImplGlfw_UpdateMousePosAndButtons() // Update mouse position const ImVec2 mouse_pos_backup = io.MousePos; io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); - //[Bruno Levy] 05/16/2016 Under emscripten, it seems that the window is never focused (so I bypass the test). -#ifndef __EMSCRIPTEN__ - if (glfwGetWindowAttrib(g_Window, GLFW_FOCUSED)) -#endif +#ifdef __EMSCRIPTEN__ + const bool focused = true; // Emscripten +#else + const bool focused = glfwGetWindowAttrib(g_Window, GLFW_FOCUSED) != 0; +#endif + if (focused) { if (io.WantSetMousePos) { diff --git a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_glfw.h b/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_glfw.h index eddd7c95..6b606c8c 100644 --- a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_glfw.h +++ b/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_glfw.h @@ -1,17 +1,17 @@ /* - * ImGui Platform Binding for: GLFW + * dear imgui: Platform Binding for GLFW * This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan..) * (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) * * Implemented features: * [X] Platform: Clipboard support. - * [X] Platform: Gamepad navigation mapping. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. + * [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. * [x] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: 3 cursors types are missing from GLFW. + * [X] Platform: Keyboard arrays indexed using GLFW_KEY_* codes, e.g. ImGui::IsKeyPressed(GLFW_KEY_SPACE). * * You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. - * If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). - * If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. - * https://github.com/ocornut/imgui + * If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp. + * https:*github.com/ocornut/imgui * * About GLSL version: * The 'glsl_version' initialization parameter defaults to "#version 150" if NULL. @@ -20,6 +20,8 @@ /* [Bruno] C-style comment */ +#pragma once + #ifndef __ANDROID__ /* [Bruno] */ /* [Bruno] */ @@ -35,9 +37,8 @@ IMGUI_IMPL_API void ImGui_ImplGlfw_Shutdown(); IMGUI_IMPL_API void ImGui_ImplGlfw_NewFrame(); /* - * GLFW callbacks (installed by default if you enable 'install_callbacks' during initialization) - * Provided here if you want to chain callbacks. - * You can also handle inputs yourself and use those as a reference. + * InitXXX function with 'install_callbacks=true': install GLFW callbacks. They will call user's previously installed callbacks, if any. + * InitXXX function with 'install_callbacks=false': do not install GLFW callbacks. You will need to call them yourself from your own GLFW callbacks. */ IMGUI_IMPL_API void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods); IMGUI_IMPL_API void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset); @@ -50,3 +51,4 @@ IMGUI_IMPL_API void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned #endif #endif /* __ANDROID__ [Bruno] */ + diff --git a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_opengl2.cpp b/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_opengl2.cpp deleted file mode 100644 index ba9ebea1..00000000 --- a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_opengl2.cpp +++ /dev/null @@ -1,233 +0,0 @@ -// ImGui Renderer for: OpenGL2 (legacy OpenGL, fixed pipeline) -// This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..) - -// Implemented features: -// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. - -// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. -// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp. -// https://github.com/ocornut/imgui - -// **DO NOT USE THIS CODE IF YOUR CODE/ENGINE IS USING MODERN OPENGL (SHADERS, VBO, VAO, etc.)** -// **Prefer using the code in imgui_impl_opengl3.cpp** -// This code is mostly provided as a reference to learn how ImGui integration works, because it is shorter to read. -// If your code is using GL3+ context or any semi modern OpenGL calls, using this is likely to make everything more -// complicated, will require your code to reset every single OpenGL attributes to their initial state, and might -// confuse your GPU driver. -// The GL2 code is unable to reset attributes or even call e.g. "glUseProgram(0)" because they don't exist in that API. - -// CHANGELOG -// (minor and older changes stripped away, please see git history for details) -// 2018-08-03: OpenGL: Disabling/restoring GL_LIGHTING and GL_COLOR_MATERIAL to increase compatibility with legacy OpenGL applications. -// 2018-06-08: Misc: Extracted imgui_impl_opengl2.cpp/.h away from the old combined GLFW/SDL+OpenGL2 examples. -// 2018-06-08: OpenGL: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle. -// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplGlfwGL2_RenderDrawData() in the .h file so you can call it yourself. -// 2017-09-01: OpenGL: Save and restore current polygon mode. -// 2016-09-10: OpenGL: Uploading font texture as RGBA32 to increase compatibility with users shaders (not ideal). -// 2016-09-05: OpenGL: Fixed save and restore of current scissor rectangle. - -#include "imgui.h" -#include "imgui_impl_opengl2.h" - -// [Bruno 05/16/2016] conditional compilation -#ifdef GEO_GL_LEGACY - -#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier -#include // intptr_t -#else -#include // intptr_t -#endif - -// Include OpenGL header (without an OpenGL loader) requires a bit of fiddling -#if defined(_WIN32) && !defined(APIENTRY) -#define APIENTRY __stdcall // It is customary to use APIENTRY for OpenGL function pointer declarations on all platforms. Additionally, the Windows OpenGL header needs APIENTRY. -#endif -#if defined(_WIN32) && !defined(WINGDIAPI) -#define WINGDIAPI __declspec(dllimport) // Some Windows OpenGL headers need this -#endif -#if defined(__APPLE__) -#include -#else -#include -#endif - -// OpenGL Data -static GLuint g_FontTexture = 0; - -// Functions -bool ImGui_ImplOpenGL2_Init() -{ - return true; -} - -void ImGui_ImplOpenGL2_Shutdown() -{ - ImGui_ImplOpenGL2_DestroyDeviceObjects(); -} - -void ImGui_ImplOpenGL2_NewFrame() -{ - if (!g_FontTexture) - ImGui_ImplOpenGL2_CreateDeviceObjects(); -} - -// OpenGL2 Render function. -// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop) -// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly, in order to be able to run within any OpenGL engine that doesn't do so. -void ImGui_ImplOpenGL2_RenderDrawData(ImDrawData* draw_data) -{ - // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) - ImGuiIO& io = ImGui::GetIO(); - int fb_width = (int)(draw_data->DisplaySize.x * io.DisplayFramebufferScale.x); - int fb_height = (int)(draw_data->DisplaySize.y * io.DisplayFramebufferScale.y); - if (fb_width == 0 || fb_height == 0) - return; - draw_data->ScaleClipRects(io.DisplayFramebufferScale); - - // We are using the OpenGL fixed pipeline to make the example code simpler to read! - // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, vertex/texcoord/color pointers, polygon fill. - GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode); - GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport); - GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box); - glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_TRANSFORM_BIT); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDisable(GL_CULL_FACE); - glDisable(GL_DEPTH_TEST); - glDisable(GL_LIGHTING); - glDisable(GL_COLOR_MATERIAL); - glEnable(GL_SCISSOR_TEST); - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); - - // [Bruno Levy] 05/18/2016 Added these missing state variables. - glDisable(GL_CLIP_PLANE0); - glActiveTexture(GL_TEXTURE0); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - glDisable(GL_LIGHTING); - glMatrixMode(GL_TEXTURE); - glPushMatrix(); - glLoadIdentity(); - - - glEnable(GL_TEXTURE_2D); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - //glUseProgram(0); // You may want this if using this code in an OpenGL 3+ context where shaders may be bound - - // Setup viewport, orthographic projection matrix - // Our visible imgui space lies from draw_data->DisplayPps (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayMin is typically (0,0) for single viewport apps. - glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height); - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glOrtho(draw_data->DisplayPos.x, draw_data->DisplayPos.x + draw_data->DisplaySize.x, draw_data->DisplayPos.y + draw_data->DisplaySize.y, draw_data->DisplayPos.y, -1.0f, +1.0f); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - - // Render command lists - ImVec2 pos = draw_data->DisplayPos; - for (int n = 0; n < draw_data->CmdListsCount; n++) - { - const ImDrawList* cmd_list = draw_data->CmdLists[n]; - const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; - const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; - glVertexPointer(2, GL_FLOAT, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, pos))); - glTexCoordPointer(2, GL_FLOAT, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, uv))); - glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, col))); - - for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) - { - const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; - if (pcmd->UserCallback) - { - // User callback (registered via ImDrawList::AddCallback) - pcmd->UserCallback(cmd_list, pcmd); - } - else - { - ImVec4 clip_rect = ImVec4(pcmd->ClipRect.x - pos.x, pcmd->ClipRect.y - pos.y, pcmd->ClipRect.z - pos.x, pcmd->ClipRect.w - pos.y); - if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f) - { - // Apply scissor/clipping rectangle - glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y)); - - // Bind texture, Draw - glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId); - glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer); - } - } - idx_buffer += pcmd->ElemCount; - } - } - - // Restore modified state - glDisableClientState(GL_COLOR_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - glDisableClientState(GL_VERTEX_ARRAY); - glBindTexture(GL_TEXTURE_2D, (GLuint)last_texture); - - glMatrixMode(GL_TEXTURE); // [Bruno Levy] 05/18/2016 Restore texture matrix. - glPopMatrix(); - - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glPopAttrib(); - glPolygonMode(GL_FRONT, (GLenum)last_polygon_mode[0]); glPolygonMode(GL_BACK, (GLenum)last_polygon_mode[1]); - glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]); - glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]); -} - -bool ImGui_ImplOpenGL2_CreateFontsTexture() -{ - // Build texture atlas - ImGuiIO& io = ImGui::GetIO(); - unsigned char* pixels; - int width, height; - io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bits (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory. - - // Upload texture to graphics system - GLint last_texture; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - glGenTextures(1, &g_FontTexture); - glBindTexture(GL_TEXTURE_2D, g_FontTexture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); - - // Store our identifier - io.Fonts->TexID = (ImTextureID)(intptr_t)g_FontTexture; - - // Restore state - glBindTexture(GL_TEXTURE_2D, last_texture); - - return true; -} - -void ImGui_ImplOpenGL2_DestroyFontsTexture() -{ - if (g_FontTexture) - { - ImGuiIO& io = ImGui::GetIO(); - glDeleteTextures(1, &g_FontTexture); - io.Fonts->TexID = 0; - g_FontTexture = 0; - } -} - -bool ImGui_ImplOpenGL2_CreateDeviceObjects() -{ - return ImGui_ImplOpenGL2_CreateFontsTexture(); -} - -void ImGui_ImplOpenGL2_DestroyDeviceObjects() -{ - ImGui_ImplOpenGL2_DestroyFontsTexture(); -} - -#endif // [Bruno Levy] conditional compile. diff --git a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_opengl2.h b/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_opengl2.h deleted file mode 100644 index b2f67580..00000000 --- a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_opengl2.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * ImGui Renderer for: OpenGL2 (legacy OpenGL, fixed pipeline) - * This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..) - * - * Implemented features: - * [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void* / ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. - * - * **DO NOT USE THIS CODE IF YOUR CODE/ENGINE IS USING MODERN OPENGL (SHADERS, VBO, VAO, etc.)** - * **Prefer using the code in imgui_impl_opengl3.cpp** - * This code is mostly provided as a reference to learn how ImGui integration works, because it is shorter to read. - * If your code is using GL3+ context or any semi modern OpenGL calls, using this is likely to make everything more - * complicated, will require your code to reset every single OpenGL attributes to their initial state, and might - * confuse your GPU driver. - * The GL2 code is unable to reset attributes or even call e.g. "glUseProgram(0)" because they don't exist in that API. - */ - -/* [Bruno] C-style comment */ - -// [Bruno 05/16/2016] conditional compilation -#include -#ifdef GEO_GL_LEGACY - -/* [Bruno] */ -#ifdef __cplusplus -extern "C" { -#endif - -IMGUI_IMPL_API bool ImGui_ImplOpenGL2_Init(); -IMGUI_IMPL_API void ImGui_ImplOpenGL2_Shutdown(); -IMGUI_IMPL_API void ImGui_ImplOpenGL2_NewFrame(); -IMGUI_IMPL_API void ImGui_ImplOpenGL2_RenderDrawData(ImDrawData* draw_data); - -/* Called by Init/NewFrame/Shutdown */ -IMGUI_IMPL_API bool ImGui_ImplOpenGL2_CreateFontsTexture(); -IMGUI_IMPL_API void ImGui_ImplOpenGL2_DestroyFontsTexture(); -IMGUI_IMPL_API bool ImGui_ImplOpenGL2_CreateDeviceObjects(); -IMGUI_IMPL_API void ImGui_ImplOpenGL2_DestroyDeviceObjects(); - -/* [Bruno] */ -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_opengl3.cpp b/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_opengl3.cpp index 8fc0a8e3..7f519509 100644 --- a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_opengl3.cpp +++ b/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_opengl3.cpp @@ -1,4 +1,4 @@ -// ImGui Renderer for: OpenGL3 / OpenGL ES2 / OpenGL ES3 (modern OpenGL with shaders / programmatic pipeline) +// dear imgui: Renderer for OpenGL3 / OpenGL ES2 / OpenGL ES3 (modern OpenGL with shaders / programmatic pipeline) // This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..) // (Note: We are using GL3W as a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc..) @@ -11,6 +11,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2018-11-13: OpenGL: Support for GL 4.5's glClipControl(GL_UPPER_LEFT). // 2018-08-29: OpenGL: Added support for more OpenGL loaders: glew and glad, with comments indicative that any loader can be used. // 2018-08-09: OpenGL: Default to OpenGL ES 3 on iOS and Android. GLSL version default to "#version 300 ES". // 2018-07-30: OpenGL: Support for GLSL 300 ES and 410 core. Fixes for Emscripten compilation. @@ -162,6 +163,12 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE); GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST); GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST); + bool clip_origin_lower_left = true; +#ifdef GL_CLIP_ORIGIN + GLenum last_clip_origin = 0; glGetIntegerv(GL_CLIP_ORIGIN, (GLint*)&last_clip_origin); // Support for GL 4.5's glClipControl(GL_UPPER_LEFT) + if (last_clip_origin == GL_UPPER_LEFT) + clip_origin_lower_left = false; +#endif // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill glEnable(GL_BLEND); @@ -175,7 +182,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) #endif // Setup viewport, orthographic projection matrix - // Our visible imgui space lies from draw_data->DisplayPps (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayMin is typically (0,0) for single viewport apps. + // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayMin is typically (0,0) for single viewport apps. glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height); float L = draw_data->DisplayPos.x; float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x; @@ -234,7 +241,10 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f) { // Apply scissor/clipping rectangle - glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y)); + if (clip_origin_lower_left) + glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y)); + else + glScissor((int)clip_rect.x, (int)clip_rect.y, (int)clip_rect.z, (int)clip_rect.w); // Support for GL 4.5's glClipControl(GL_UPPER_LEFT) // Bind texture, Draw glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId); @@ -308,13 +318,13 @@ void ImGui_ImplOpenGL3_DestroyFontsTexture() } } -// If you get an error please report on github. You may try different GL context version or GLSL version. +// If you get an error please report on github. You may try different GL context version or GLSL version. See GL<>GLSL version table at the top of this file. static bool CheckShader(GLuint handle, const char* desc) { GLint status = 0, log_length = 0; glGetShaderiv(handle, GL_COMPILE_STATUS, &status); glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &log_length); - if (status == GL_FALSE) + if ((GLboolean)status == GL_FALSE) fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to compile %s!\n", desc); if (log_length > 0) { @@ -323,17 +333,17 @@ static bool CheckShader(GLuint handle, const char* desc) glGetShaderInfoLog(handle, log_length, NULL, (GLchar*)buf.begin()); fprintf(stderr, "%s\n", buf.begin()); } - return status == GL_TRUE; + return (GLboolean)status == GL_TRUE; } -// If you get an error please report on github. You may try different GL context version or GLSL version. +// If you get an error please report on GitHub. You may try different GL context version or GLSL version. static bool CheckProgram(GLuint handle, const char* desc) { GLint status = 0, log_length = 0; glGetProgramiv(handle, GL_LINK_STATUS, &status); glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &log_length); - if (status == GL_FALSE) - fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to link %s!\n", desc); + if ((GLboolean)status == GL_FALSE) + fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to link %s! (with GLSL '%s')\n", desc, g_GlslVersionString); if (log_length > 0) { ImVector buf; @@ -341,7 +351,7 @@ static bool CheckProgram(GLuint handle, const char* desc) glGetProgramInfoLog(handle, log_length, NULL, (GLchar*)buf.begin()); fprintf(stderr, "%s\n", buf.begin()); } - return status == GL_TRUE; + return (GLboolean)status == GL_TRUE; } bool ImGui_ImplOpenGL3_CreateDeviceObjects() diff --git a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_opengl3.h b/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_opengl3.h index 1f2ad948..1e44b545 100644 --- a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_opengl3.h +++ b/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_opengl3.h @@ -1,5 +1,5 @@ /* - * ImGui Renderer for: OpenGL3 / OpenGL ES2 / OpenGL ES3 (modern OpenGL with shaders / programmatic pipeline) + * dear imgui: Renderer for OpenGL3 / OpenGL ES2 / OpenGL ES3 (modern OpenGL with shaders / programmatic pipeline) * This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..) * (Note: We are using GL3W as a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc..) * @@ -8,7 +8,7 @@ * * You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. * If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp. - * https://github.com/ocornut/imgui + * https: *github.com/ocornut/imgui * * About OpenGL function loaders: * About OpenGL function loaders: modern OpenGL doesn't have a standard header file and requires individual function pointers to be loaded manually. @@ -20,8 +20,10 @@ * On computer platform the GLSL version default to "#version 130". On OpenGL ES 3 platform it defaults to "#version 300 es" * Only override if your GL version doesn't handle this GLSL version. See GLSL version table at the top of imgui_impl_opengl3.cpp. */ - + /* [Bruno] C-style comment */ + +#pragma once /* [Bruno] */ #ifdef __cplusplus @@ -35,9 +37,8 @@ extern "C" { && !defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM) #define IMGUI_IMPL_OPENGL_LOADER_GL3W #endif - - -IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = "#version 150"); + +IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = NULL); IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown(); IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame(); IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data); @@ -52,4 +53,4 @@ IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects(); #ifdef __cplusplus } #endif - + diff --git a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_win32.cpp b/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_win32.cpp index f6350a95..4a6276c5 100644 --- a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_win32.cpp +++ b/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_win32.cpp @@ -1,10 +1,12 @@ -// ImGui Platform Binding for: Windows (standard windows API for 32 and 64 bits applications) +// dear imgui: Platform Binding for Windows (standard windows API for 32 and 64 bits applications) // This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..) // Implemented features: // [X] Platform: Clipboard support (for Win32 this is actually part of core imgui) // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. // [X] Platform: Keyboard arrays indexed using VK_* Virtual Key Codes, e.g. ImGui::IsKeyPressed(VK_SPACE). +// Missing features: +// [ ] Platform: Gamepad support (best leaving it to user application to fill io.NavInputs[] with gamepad inputs from their source of choice). #ifdef _WIN32 /* [Bruno] */ @@ -247,3 +249,4 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA } #endif /* [Bruno] */ + diff --git a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_win32.h b/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_win32.h index 275d7709..7f687ed8 100644 --- a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_win32.h +++ b/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_win32.h @@ -1,14 +1,19 @@ /* - * ImGui Platform Binding for: Windows (standard windows API for 32 and 64 bits applications) + * dear imgui: Platform Binding for Windows (standard windows API for 32 and 64 bits applications) * This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..) * * Implemented features: * [X] Platform: Clipboard support (for Win32 this is actually part of core imgui) * [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. + * [X] Platform: Keyboard arrays indexed using VK_* Virtual Key Codes, e.g. ImGui::IsKeyPressed(VK_SPACE). + * Missing features: + * [ ] Platform: Gamepad support (best leaving it to user application to fill io.NavInputs[] with gamepad inputs from their source of choice). */ /* [Bruno] C-style comment */ +#pragma once + /* [Bruno] */ #ifdef _WIN32 #ifdef __cplusplus @@ -25,6 +30,7 @@ IMGUI_IMPL_API void ImGui_ImplWin32_NewFrame(); * You may or not need this for your implementation, but it can serve as reference for handling inputs. * Intentionally commented out to avoid dragging dependencies on types. You can copy the extern declaration in your code. */ + /* IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); */ @@ -34,3 +40,4 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPAR } #endif #endif + diff --git a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_internal.h b/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_internal.h index 0b9fec23..ca90975f 100644 --- a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_internal.h +++ b/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.64 +// dear imgui, v1.66 // (internal structures/api) // You may use this file to debug, understand or extend ImGui features but we don't provide any guarantee of forward compatibility! @@ -63,6 +63,7 @@ typedef int ImGuiNavDirSourceFlags; // -> enum ImGuiNavDirSourceFlags_ // Flags: typedef int ImGuiNavMoveFlags; // -> enum ImGuiNavMoveFlags_ // Flags: for navigation requests typedef int ImGuiSeparatorFlags; // -> enum ImGuiSeparatorFlags_ // Flags: for Separator() - internal typedef int ImGuiSliderFlags; // -> enum ImGuiSliderFlags_ // Flags: for SliderBehavior() +typedef int ImGuiDragFlags; // -> enum ImGuiDragFlags_ // Flags: for DragBehavior() //------------------------------------------------------------------------- // STB libraries @@ -76,7 +77,7 @@ namespace ImGuiStb #define STB_TEXTEDIT_STRING ImGuiInputTextState #define STB_TEXTEDIT_CHARTYPE ImWchar #define STB_TEXTEDIT_GETWIDTH_NEWLINE -1.0f -#include "stb_textedit.h" +#include "imstb_textedit.h" } // namespace ImGuiStb @@ -132,6 +133,7 @@ IMGUI_API ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, con IMGUI_API bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p); IMGUI_API ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p); IMGUI_API void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w); +IMGUI_API ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy); // Helpers: String IMGUI_API int ImStricmp(const char* str1, const char* str2); @@ -140,7 +142,8 @@ IMGUI_API void ImStrncpy(char* dst, const char* src, size_t count); IMGUI_API char* ImStrdup(const char* str); IMGUI_API const char* ImStrchrRange(const char* str_begin, const char* str_end, char c); IMGUI_API int ImStrlenW(const ImWchar* str); -IMGUI_API const ImWchar*ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin); // Find beginning-of-line +IMGUI_API const char* ImStreolRange(const char* str, const char* str_end); // End end-of-line +IMGUI_API const ImWchar*ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin); // Find beginning-of-line IMGUI_API const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end); IMGUI_API void ImStrTrimBlanks(char* str); IMGUI_API int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...) IM_FMTARGS(3); @@ -247,6 +250,12 @@ enum ImGuiSliderFlags_ ImGuiSliderFlags_Vertical = 1 << 0 }; +enum ImGuiDragFlags_ +{ + ImGuiDragFlags_None = 0, + ImGuiDragFlags_Vertical = 1 << 0 +}; + enum ImGuiColumnsFlags_ { // Default: 0 @@ -648,7 +657,8 @@ struct ImGuiContext int FrameCount; int FrameCountEnded; int FrameCountRendered; - ImVector Windows; + ImVector Windows; // Windows, sorted in display order, back to front + ImVector WindowsFocusOrder; // Windows, sorted in focus order, back to front ImVector WindowsSortBuffer; ImVector CurrentWindowStack; ImGuiStorage WindowsById; @@ -659,7 +669,8 @@ struct ImGuiContext ImGuiID HoveredId; // Hovered widget bool HoveredIdAllowOverlap; ImGuiID HoveredIdPreviousFrame; - float HoveredIdTimer; + float HoveredIdTimer; // Measure contiguous hovering time + float HoveredIdNotActiveTimer; // Measure contiguous hovering time where the item has not been active ImGuiID ActiveId; // Active widget ImGuiID ActiveIdPreviousFrame; ImGuiID ActiveIdIsAlive; // Active widget has been seen this frame (we can't use a bool as the ActiveId may change within the frame) @@ -676,8 +687,9 @@ struct ImGuiContext ImGuiInputSource ActiveIdSource; // Activating with mouse or nav (gamepad/keyboard) ImGuiID LastActiveId; // Store the last non-zero ActiveId, useful for animation. float LastActiveIdTimer; // Store the last non-zero ActiveId timer since the beginning of activation, useful for animation. + ImVec2 LastValidMousePos; ImGuiWindow* MovingWindow; // Track the window we clicked on (in order to preserve focus). The actually window that is moved is generally MovingWindow->RootWindow. - ImVector ColorModifiers; // Stack for PushStyleColor()/PopStyleColor() + ImVector ColorModifiers; // Stack for PushStyleColor()/PopStyleColor() ImVector StyleModifiers; // Stack for PushStyleVar()/PopStyleVar() ImVector FontStack; // Stack for PushFont()/PopFont() ImVector OpenPopupStack; // Which popups are open (persistent) @@ -806,7 +818,7 @@ struct ImGuiContext HoveredId = 0; HoveredIdAllowOverlap = false; HoveredIdPreviousFrame = 0; - HoveredIdTimer = 0.0f; + HoveredIdTimer = HoveredIdNotActiveTimer = 0.0f; ActiveId = 0; ActiveIdPreviousFrame = 0; ActiveIdIsAlive = 0; @@ -822,6 +834,7 @@ struct ImGuiContext ActiveIdSource = ImGuiInputSource_None; LastActiveId = 0; LastActiveIdTimer = 0.0f; + LastValidMousePos = ImVec2(0.0f, 0.0f); MovingWindow = NULL; NextTreeNodeOpenVal = false; NextTreeNodeOpenCond = 0; @@ -896,13 +909,13 @@ struct ImGuiContext // This is going to be exposed in imgui.h when stabilized enough. enum ImGuiItemFlags_ { - ImGuiItemFlags_AllowKeyboardFocus = 1 << 0, // true + ImGuiItemFlags_NoTabStop = 1 << 0, // false ImGuiItemFlags_ButtonRepeat = 1 << 1, // false // Button() will return true multiple times based on io.KeyRepeatDelay and io.KeyRepeatRate settings. ImGuiItemFlags_Disabled = 1 << 2, // false // [BETA] Disable interactions but doesn't affect visuals yet. See github.com/ocornut/imgui/issues/211 ImGuiItemFlags_NoNav = 1 << 3, // false ImGuiItemFlags_NoNavDefaultFocus = 1 << 4, // false ImGuiItemFlags_SelectableDontClosePopup = 1 << 5, // false // MenuItem/Selectable() automatically closes current Popup window - ImGuiItemFlags_Default_ = ImGuiItemFlags_AllowKeyboardFocus + ImGuiItemFlags_Default_ = 0 }; // Transient per-window data, reset at the beginning of the frame. This used to be called ImGuiDrawContext, hence the DC variable name in ImGuiWindow. @@ -1015,9 +1028,9 @@ struct IMGUI_API ImGuiWindow bool Appearing; // Set during the frame where the window is appearing (or re-appearing) bool Hidden; // Do not display (== (HiddenFramesForResize > 0) || bool HasCloseButton; // Set when the window has a close button (p_open != NULL) + int BeginCount; // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs) int BeginOrderWithinParent; // Order within immediate parent window, if we are a child window. Otherwise 0. int BeginOrderWithinContext; // Order within entire imgui context. This is mostly used for debugging submission order related issues. - int BeginCount; // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs) ImGuiID PopupId; // ID in the popup stack when this window is used as a popup/menu (because we use generic Name/ID for recycling) int AutoFitFramesX, AutoFitFramesY; bool AutoFitOnlyGrows; @@ -1110,17 +1123,21 @@ namespace ImGui // - You are calling ImGui functions after ImGui::EndFrame()/ImGui::Render() and before the next ImGui::NewFrame(), which is also illegal. inline ImGuiWindow* GetCurrentWindowRead() { ImGuiContext& g = *GImGui; return g.CurrentWindow; } inline ImGuiWindow* GetCurrentWindow() { ImGuiContext& g = *GImGui; g.CurrentWindow->WriteAccessed = true; return g.CurrentWindow; } + IMGUI_API ImGuiWindow* FindWindowByID(ImGuiID id); IMGUI_API ImGuiWindow* FindWindowByName(const char* name); IMGUI_API void FocusWindow(ImGuiWindow* window); - IMGUI_API void FocusFrontMostActiveWindowIgnoringOne(ImGuiWindow* ignore_window); - IMGUI_API void BringWindowToFront(ImGuiWindow* window); - IMGUI_API void BringWindowToBack(ImGuiWindow* window); + IMGUI_API void FocusPreviousWindowIgnoringOne(ImGuiWindow* ignore_window); + IMGUI_API void BringWindowToFocusFront(ImGuiWindow* window); + IMGUI_API void BringWindowToDisplayFront(ImGuiWindow* window); + IMGUI_API void BringWindowToDisplayBack(ImGuiWindow* window); IMGUI_API void UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window); IMGUI_API ImVec2 CalcWindowExpectedSize(ImGuiWindow* window); IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent); IMGUI_API bool IsWindowNavFocusable(ImGuiWindow* window); IMGUI_API void SetWindowScrollX(ImGuiWindow* window, float new_scroll_x); IMGUI_API void SetWindowScrollY(ImGuiWindow* window, float new_scroll_y); + IMGUI_API float GetWindowScrollMaxX(ImGuiWindow* window); + IMGUI_API float GetWindowScrollMaxY(ImGuiWindow* window); IMGUI_API ImRect GetWindowAllowedExtentRect(ImGuiWindow* window); IMGUI_API void SetCurrentFont(ImFont* font); inline ImFont* GetDefaultFont() { ImGuiContext& g = *GImGui; return g.IO.FontDefault ? g.IO.FontDefault : g.IO.Fonts->Fonts[0]; } @@ -1239,20 +1256,31 @@ namespace ImGui // Widgets low-level behaviors IMGUI_API bool ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags = 0); - IMGUI_API bool DragBehavior(ImGuiID id, ImGuiDataType data_type, void* v, float v_speed, const void* v_min, const void* v_max, const char* format, float power); + IMGUI_API bool DragBehavior(ImGuiID id, ImGuiDataType data_type, void* v, float v_speed, const void* v_min, const void* v_max, const char* format, float power, ImGuiDragFlags flags); IMGUI_API bool SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* v, const void* v_min, const void* v_max, const char* format, float power, ImGuiSliderFlags flags, ImRect* out_grab_bb); IMGUI_API bool SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend = 0.0f, float hover_visibility_delay = 0.0f); IMGUI_API bool TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end = NULL); IMGUI_API bool TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags = 0); // Consume previous SetNextTreeNodeOpened() data, if any. May return true when logging IMGUI_API void TreePushRawID(ImGuiID id); + // Template functions are instantiated in imgui_widgets.cpp for a finite number of types. + // To use them externally (for custom widget) you may need an "extern template" statement in your code in order to link to existing instances and silence Clang warnings (see #2036). + // e.g. " extern template IMGUI_API float RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, float v); " + template IMGUI_API bool DragBehaviorT(ImGuiDataType data_type, T* v, float v_speed, const T v_min, const T v_max, const char* format, float power, ImGuiDragFlags flags); + template IMGUI_API bool SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, T* v, const T v_min, const T v_max, const char* format, float power, ImGuiSliderFlags flags, ImRect* out_grab_bb); + template IMGUI_API float SliderCalcRatioFromValueT(ImGuiDataType data_type, T v, T v_min, T v_max, float power, float linear_zero_pos); + template IMGUI_API T RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, T v); + + // InputText IMGUI_API bool InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); IMGUI_API bool InputScalarAsWidgetReplacement(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* data_ptr, const char* format); + // Color IMGUI_API void ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags); IMGUI_API void ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags); IMGUI_API void ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags flags); + // Plot IMGUI_API void PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size); // Shade functions (write over already created vertices) diff --git a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_widgets.cpp b/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_widgets.cpp index c2c67e78..7a24cfa6 100644 --- a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_widgets.cpp +++ b/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_widgets.cpp @@ -1,24 +1,27 @@ -// dear imgui, v1.64 +// dear imgui, v1.66 // (widgets code) /* Index of this file: -- Widgets: Text, etc. -- Widgets: Button, Image, Checkbox, RadioButton, ProgressBar, Bullet, etc. -- Widgets: ComboBox -- Data Type and Data Formatting Helpers -- Widgets: DragScalar, DragFloat, DragInt, etc. -- Widgets: SliderScalar, SliderFloat, SliderInt, etc. -- Widgets: InputScalar, InputFloat, InputInt, etc. -- Widgets: InputText, InputTextMultiline -- Widgets: ColorEdit, ColorPicker, ColorButton, etc. -- Widgets: TreeNode, TreePush, TreePop, etc. -- Widgets: Selectable -- Widgets: ListBox -- Widgets: PlotLines, PlotHistogram -- Widgets: Value -- Widgets: MenuItem, BeginMenu, EndMenu, etc. + +// [SECTION] Forward Declarations +// [SECTION] Widgets: Text, etc. +// [SECTION] Widgets: Main (Button, Image, Checkbox, RadioButton, ProgressBar, Bullet, etc.) +// [SECTION] Widgets: Low-level Layout helpers (Spacing, Dummy, NewLine, Separator, etc.) +// [SECTION] Widgets: ComboBox +// [SECTION] Data Type and Data Formatting Helpers +// [SECTION] Widgets: DragScalar, DragFloat, DragInt, etc. +// [SECTION] Widgets: SliderScalar, SliderFloat, SliderInt, etc. +// [SECTION] Widgets: InputScalar, InputFloat, InputInt, etc. +// [SECTION] Widgets: InputText, InputTextMultiline +// [SECTION] Widgets: ColorEdit, ColorPicker, ColorButton, etc. +// [SECTION] Widgets: TreeNode, CollapsingHeader, etc. +// [SECTION] Widgets: Selectable +// [SECTION] Widgets: ListBox +// [SECTION] Widgets: PlotLines, PlotHistogram +// [SECTION] Widgets: Value helpers +// [SECTION] Widgets: MenuItem, BeginMenu, EndMenu, etc. */ @@ -80,7 +83,7 @@ static const ImU64 IM_U64_MAX = (2ULL * 9223372036854775807LL + 1); #endif //------------------------------------------------------------------------- -// Forward Declarations +// [SECTION] Forward Declarations //------------------------------------------------------------------------- // Data Type helpers @@ -93,20 +96,9 @@ static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInpu static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end); static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false); -namespace ImGui -{ - -// Template widget behaviors -template -static bool DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const TYPE v_min, const TYPE v_max, const char* format, float power); - -template -static bool SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, TYPE* v, const TYPE v_min, const TYPE v_max, const char* format, float power, ImGuiSliderFlags flags, ImRect* out_grab_bb); - -} - //------------------------------------------------------------------------- -// WIDGETS: Text +// [SECTION] Widgets: Text, etc. +//------------------------------------------------------------------------- // - TextUnformatted() // - Text() // - TextV() @@ -141,8 +133,9 @@ void ImGui::TextUnformatted(const char* text, const char* text_end) { // Long text! // Perform manual coarse clipping to optimize for long multi-line text - // From this point we will only compute the width of lines that are visible. Optimization only available when word-wrapping is disabled. - // We also don't vertically center the text within the line full height, which is unlikely to matter because we are likely the biggest and only item on the line. + // - From this point we will only compute the width of lines that are visible. Optimization only available when word-wrapping is disabled. + // - We also don't vertically center the text within the line full height, which is unlikely to matter because we are likely the biggest and only item on the line. + // - We use memchr(), pay attention that well optimized versions of those str/mem functions are much faster than a casually written loop. const char* line = text; const float line_height = GetTextLineHeight(); const ImRect clip_rect = window->ClipRect; @@ -161,7 +154,7 @@ void ImGui::TextUnformatted(const char* text, const char* text_end) int lines_skipped = 0; while (line < text_end && lines_skipped < lines_skippable) { - const char* line_end = strchr(line, '\n'); + const char* line_end = (const char*)memchr(line, '\n', text_end - line); if (!line_end) line_end = text_end; line = line_end + 1; @@ -177,15 +170,15 @@ void ImGui::TextUnformatted(const char* text, const char* text_end) ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height)); while (line < text_end) { - const char* line_end = strchr(line, '\n'); if (IsClippedEx(line_rect, 0, false)) break; + const char* line_end = (const char*)memchr(line, '\n', text_end - line); + if (!line_end) + line_end = text_end; const ImVec2 line_size = CalcTextSize(line, line_end, false); text_size.x = ImMax(text_size.x, line_size.x); RenderText(pos, line, line_end, false); - if (!line_end) - line_end = text_end; line = line_end + 1; line_rect.Min.y += line_height; line_rect.Max.y += line_height; @@ -196,7 +189,7 @@ void ImGui::TextUnformatted(const char* text, const char* text_end) int lines_skipped = 0; while (line < text_end) { - const char* line_end = strchr(line, '\n'); + const char* line_end = (const char*)memchr(line, '\n', text_end - line); if (!line_end) line_end = text_end; line = line_end + 1; @@ -361,7 +354,8 @@ void ImGui::BulletTextV(const char* fmt, va_list args) } //------------------------------------------------------------------------- -// WIDGETS: Main +// [SECTION] Widgets: Main +//------------------------------------------------------------------------- // - ButtonBehavior() [Internal] // - Button() // - SmallButton() @@ -633,7 +627,7 @@ bool ImGui::ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size, ImGuiBu const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); RenderNavHighlight(bb, id); RenderFrame(bb.Min, bb.Max, col, true, g.Style.FrameRounding); - RenderArrow(bb.Min + ImVec2(ImMax(0.0f, size.x - g.FontSize - g.Style.FramePadding.x), ImMax(0.0f, size.y - g.FontSize - g.Style.FramePadding.y)), dir); + RenderArrow(bb.Min + ImVec2(ImMax(0.0f, (size.x - g.FontSize) * 0.5f), ImMax(0.0f, (size.y - g.FontSize) * 0.5f)), dir); return pressed; } @@ -845,7 +839,7 @@ bool ImGui::ImageButton(ImTextureID user_texture_id, const ImVec2& size, const I // Default to using texture ID as ID. User can still push string/integer prefixes. // We could hash the size/uv to create a unique ID but that would prevent the user from animating UV. - PushID((void*)user_texture_id); + PushID((void*)(intptr_t)user_texture_id); const ImGuiID id = window->GetID("#image"); PopID(); @@ -1064,7 +1058,191 @@ void ImGui::Bullet() } //------------------------------------------------------------------------- -// WIDGETS: Combo Box +// [SECTION] Widgets: Low-level Layout helpers +//------------------------------------------------------------------------- +// - Spacing() +// - Dummy() +// - NewLine() +// - AlignTextToFramePadding() +// - Separator() +// - VerticalSeparator() [Internal] +// - SplitterBehavior() [Internal] +//------------------------------------------------------------------------- + +void ImGui::Spacing() +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + ItemSize(ImVec2(0,0)); +} + +void ImGui::Dummy(const ImVec2& size) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); + ItemSize(bb); + ItemAdd(bb, 0); +} + +void ImGui::NewLine() +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + const ImGuiLayoutType backup_layout_type = window->DC.LayoutType; + window->DC.LayoutType = ImGuiLayoutType_Vertical; + if (window->DC.CurrentLineSize.y > 0.0f) // In the event that we are on a line with items that is smaller that FontSize high, we will preserve its height. + ItemSize(ImVec2(0,0)); + else + ItemSize(ImVec2(0.0f, g.FontSize)); + window->DC.LayoutType = backup_layout_type; +} + +void ImGui::AlignTextToFramePadding() +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + window->DC.CurrentLineSize.y = ImMax(window->DC.CurrentLineSize.y, g.FontSize + g.Style.FramePadding.y * 2); + window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.CurrentLineTextBaseOffset, g.Style.FramePadding.y); +} + +// Horizontal/vertical separating line +void ImGui::Separator() +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + ImGuiContext& g = *GImGui; + + // Those flags should eventually be overridable by the user + ImGuiSeparatorFlags flags = (window->DC.LayoutType == ImGuiLayoutType_Horizontal) ? ImGuiSeparatorFlags_Vertical : ImGuiSeparatorFlags_Horizontal; + IM_ASSERT(ImIsPowerOfTwo((int)(flags & (ImGuiSeparatorFlags_Horizontal | ImGuiSeparatorFlags_Vertical)))); // Check that only 1 option is selected + if (flags & ImGuiSeparatorFlags_Vertical) + { + VerticalSeparator(); + return; + } + + // Horizontal Separator + if (window->DC.ColumnsSet) + PopClipRect(); + + float x1 = window->Pos.x; + float x2 = window->Pos.x + window->Size.x; + if (!window->DC.GroupStack.empty()) + x1 += window->DC.Indent.x; + + const ImRect bb(ImVec2(x1, window->DC.CursorPos.y), ImVec2(x2, window->DC.CursorPos.y+1.0f)); + ItemSize(ImVec2(0.0f, 0.0f)); // NB: we don't provide our width so that it doesn't get feed back into AutoFit, we don't provide height to not alter layout. + if (!ItemAdd(bb, 0)) + { + if (window->DC.ColumnsSet) + PushColumnClipRect(); + return; + } + + window->DrawList->AddLine(bb.Min, ImVec2(bb.Max.x,bb.Min.y), GetColorU32(ImGuiCol_Separator)); + + if (g.LogEnabled) + LogRenderedText(NULL, IM_NEWLINE "--------------------------------"); + + if (window->DC.ColumnsSet) + { + PushColumnClipRect(); + window->DC.ColumnsSet->LineMinY = window->DC.CursorPos.y; + } +} + +void ImGui::VerticalSeparator() +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + ImGuiContext& g = *GImGui; + + float y1 = window->DC.CursorPos.y; + float y2 = window->DC.CursorPos.y + window->DC.CurrentLineSize.y; + const ImRect bb(ImVec2(window->DC.CursorPos.x, y1), ImVec2(window->DC.CursorPos.x + 1.0f, y2)); + ItemSize(ImVec2(bb.GetWidth(), 0.0f)); + if (!ItemAdd(bb, 0)) + return; + + window->DrawList->AddLine(ImVec2(bb.Min.x, bb.Min.y), ImVec2(bb.Min.x, bb.Max.y), GetColorU32(ImGuiCol_Separator)); + if (g.LogEnabled) + LogText(" |"); +} + +// Using 'hover_visibility_delay' allows us to hide the highlight and mouse cursor for a short time, which can be convenient to reduce visual noise. +bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend, float hover_visibility_delay) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags; + window->DC.ItemFlags |= ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus; + bool item_add = ItemAdd(bb, id); + window->DC.ItemFlags = item_flags_backup; + if (!item_add) + return false; + + bool hovered, held; + ImRect bb_interact = bb; + bb_interact.Expand(axis == ImGuiAxis_Y ? ImVec2(0.0f, hover_extend) : ImVec2(hover_extend, 0.0f)); + ButtonBehavior(bb_interact, id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap); + if (g.ActiveId != id) + SetItemAllowOverlap(); + + if (held || (g.HoveredId == id && g.HoveredIdPreviousFrame == id && g.HoveredIdTimer >= hover_visibility_delay)) + SetMouseCursor(axis == ImGuiAxis_Y ? ImGuiMouseCursor_ResizeNS : ImGuiMouseCursor_ResizeEW); + + ImRect bb_render = bb; + if (held) + { + ImVec2 mouse_delta_2d = g.IO.MousePos - g.ActiveIdClickOffset - bb_interact.Min; + float mouse_delta = (axis == ImGuiAxis_Y) ? mouse_delta_2d.y : mouse_delta_2d.x; + + // Minimum pane size + float size_1_maximum_delta = ImMax(0.0f, *size1 - min_size1); + float size_2_maximum_delta = ImMax(0.0f, *size2 - min_size2); + if (mouse_delta < -size_1_maximum_delta) + mouse_delta = -size_1_maximum_delta; + if (mouse_delta > size_2_maximum_delta) + mouse_delta = size_2_maximum_delta; + + // Apply resize + if (mouse_delta != 0.0f) + { + if (mouse_delta < 0.0f) + IM_ASSERT(*size1 + mouse_delta >= min_size1); + if (mouse_delta > 0.0f) + IM_ASSERT(*size2 - mouse_delta >= min_size2); + *size1 += mouse_delta; + *size2 -= mouse_delta; + bb_render.Translate((axis == ImGuiAxis_X) ? ImVec2(mouse_delta, 0.0f) : ImVec2(0.0f, mouse_delta)); + MarkItemEdited(id); + } + } + + // Render + const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : (hovered && g.HoveredIdTimer >= hover_visibility_delay) ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator); + window->DrawList->AddRectFilled(bb_render.Min, bb_render.Max, col, g.Style.FrameRounding); + + return held; +} + + +//------------------------------------------------------------------------- +// [SECTION] Widgets: ComboBox +//------------------------------------------------------------------------- // - BeginCombo() // - EndCombo() // - Combo() @@ -1278,7 +1456,8 @@ bool ImGui::Combo(const char* label, int* current_item, const char* items_separa } //------------------------------------------------------------------------- -// WIDGETS: Data Type and Data Formatting Helpers [Internal] +// [SECTION] Data Type and Data Formatting Helpers [Internal] +//------------------------------------------------------------------------- // - PatchFormatStringFloatToInt() // - DataTypeFormatString() // - DataTypeApplyOp() @@ -1489,7 +1668,7 @@ static const char* ImAtoi(const char* src, TYPE* output) } template -static inline TYPE RoundScalarWithFormat(const char* format, ImGuiDataType data_type, TYPE v) +TYPE ImGui::RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, TYPE v) { const char* fmt_start = ImParseFormatFindStart(format); if (fmt_start[0] != '%' || fmt_start[1] == '%') // Don't apply if the value is not visible in the format string @@ -1507,7 +1686,8 @@ static inline TYPE RoundScalarWithFormat(const char* format, ImGuiDataType data_ } //------------------------------------------------------------------------- -// WIDGETS: Drags +// [SECTION] Widgets: DragScalar, DragFloat, DragInt, etc. +//------------------------------------------------------------------------- // - DragBehaviorT<>() [Internal] // - DragBehavior() [Internal] // - DragScalar() @@ -1526,33 +1706,39 @@ static inline TYPE RoundScalarWithFormat(const char* format, ImGuiDataType data_ // This is called by DragBehavior() when the widget is active (held by mouse or being manipulated with Nav controls) template -static bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const TYPE v_min, const TYPE v_max, const char* format, float power) +bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const TYPE v_min, const TYPE v_max, const char* format, float power, ImGuiDragFlags flags) { ImGuiContext& g = *GImGui; + const ImGuiAxis axis = (flags & ImGuiDragFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X; + const bool is_decimal = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double); + const bool has_min_max = (v_min != v_max); // Default tweak speed - bool has_min_max = (v_min != v_max) && (v_max - v_max < FLT_MAX); - if (v_speed == 0.0f && has_min_max) + if (v_speed == 0.0f && has_min_max && (v_max - v_min < FLT_MAX)) v_speed = (float)((v_max - v_min) * g.DragSpeedDefaultRatio); // Inputs accumulates into g.DragCurrentAccum, which is flushed into the current value as soon as it makes a difference with our precision settings float adjust_delta = 0.0f; if (g.ActiveIdSource == ImGuiInputSource_Mouse && IsMousePosValid() && g.IO.MouseDragMaxDistanceSqr[0] > 1.0f*1.0f) { - adjust_delta = g.IO.MouseDelta.x; + adjust_delta = g.IO.MouseDelta[axis]; if (g.IO.KeyAlt) - adjust_delta *= 1.0f/100.0f; + adjust_delta *= 1.0f / 100.0f; if (g.IO.KeyShift) adjust_delta *= 10.0f; } else if (g.ActiveIdSource == ImGuiInputSource_Nav) { - int decimal_precision = (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) ? ImParseFormatPrecision(format, 3) : 0; - adjust_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard|ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_RepeatFast, 1.0f/10.0f, 10.0f).x; + int decimal_precision = is_decimal ? ImParseFormatPrecision(format, 3) : 0; + adjust_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard | ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_RepeatFast, 1.0f / 10.0f, 10.0f)[axis]; v_speed = ImMax(v_speed, GetMinimumStepAtDecimalPrecision(decimal_precision)); } adjust_delta *= v_speed; + // For vertical drag we currently assume that Up=higher value (like we do with vertical sliders). This may become a parameter. + if (axis == ImGuiAxis_Y) + adjust_delta = -adjust_delta; + // Clear current value on activation // Avoid altering values and clamping when we are _already_ past the limits and heading in the same direction, so e.g. if range is 0..255, current value is 300 and we are pushing to the right side, keep the 300. bool is_just_activated = g.ActiveIdIsJustActivated; @@ -1574,7 +1760,7 @@ static bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed TYPE v_cur = *v; FLOATTYPE v_old_ref_for_accum_remainder = (FLOATTYPE)0.0f; - const bool is_power = (power != 1.0f && (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) && has_min_max); + const bool is_power = (power != 1.0f && is_decimal && has_min_max && (v_max - v_min < FLT_MAX)); if (is_power) { // Offset + round to user desired precision, with a curve on the v_min..v_max range to get more precision on one side of the range @@ -1589,7 +1775,7 @@ static bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed } // Round to user desired precision based on format string - v_cur = RoundScalarWithFormat(format, data_type, v_cur); + v_cur = RoundScalarWithFormatT(format, data_type, v_cur); // Preserve remainder after rounding has been applied. This also allow slow tweaking of values. g.DragCurrentAccumDirty = false; @@ -1607,12 +1793,12 @@ static bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed if (v_cur == (TYPE)-0) v_cur = (TYPE)0; - // Clamp values (handle overflow/wrap-around) + // Clamp values (+ handle overflow/wrap-around for integer types) if (*v != v_cur && has_min_max) { - if (v_cur < v_min || (v_cur > *v && adjust_delta < 0.0f)) + if (v_cur < v_min || (v_cur > *v && adjust_delta < 0.0f && !is_decimal)) v_cur = v_min; - if (v_cur > v_max || (v_cur < *v && adjust_delta > 0.0f)) + if (v_cur > v_max || (v_cur < *v && adjust_delta > 0.0f && !is_decimal)) v_cur = v_max; } @@ -1623,7 +1809,7 @@ static bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed return true; } -bool ImGui::DragBehavior(ImGuiID id, ImGuiDataType data_type, void* v, float v_speed, const void* v_min, const void* v_max, const char* format, float power) +bool ImGui::DragBehavior(ImGuiID id, ImGuiDataType data_type, void* v, float v_speed, const void* v_min, const void* v_max, const char* format, float power, ImGuiDragFlags flags) { ImGuiContext& g = *GImGui; if (g.ActiveId == id) @@ -1638,12 +1824,12 @@ bool ImGui::DragBehavior(ImGuiID id, ImGuiDataType data_type, void* v, float v_s switch (data_type) { - case ImGuiDataType_S32: return DragBehaviorT(data_type, (ImS32*)v, v_speed, v_min ? *(const ImS32* )v_min : IM_S32_MIN, v_max ? *(const ImS32* )v_max : IM_S32_MAX, format, power); - case ImGuiDataType_U32: return DragBehaviorT(data_type, (ImU32*)v, v_speed, v_min ? *(const ImU32* )v_min : IM_U32_MIN, v_max ? *(const ImU32* )v_max : IM_U32_MAX, format, power); - case ImGuiDataType_S64: return DragBehaviorT(data_type, (ImS64*)v, v_speed, v_min ? *(const ImS64* )v_min : IM_S64_MIN, v_max ? *(const ImS64* )v_max : IM_S64_MAX, format, power); - case ImGuiDataType_U64: return DragBehaviorT(data_type, (ImU64*)v, v_speed, v_min ? *(const ImU64* )v_min : IM_U64_MIN, v_max ? *(const ImU64* )v_max : IM_U64_MAX, format, power); - case ImGuiDataType_Float: return DragBehaviorT(data_type, (float*)v, v_speed, v_min ? *(const float* )v_min : -FLT_MAX, v_max ? *(const float* )v_max : FLT_MAX, format, power); - case ImGuiDataType_Double: return DragBehaviorT(data_type, (double*)v, v_speed, v_min ? *(const double*)v_min : -DBL_MAX, v_max ? *(const double*)v_max : DBL_MAX, format, power); + case ImGuiDataType_S32: return DragBehaviorT(data_type, (ImS32*)v, v_speed, v_min ? *(const ImS32* )v_min : IM_S32_MIN, v_max ? *(const ImS32* )v_max : IM_S32_MAX, format, power, flags); + case ImGuiDataType_U32: return DragBehaviorT(data_type, (ImU32*)v, v_speed, v_min ? *(const ImU32* )v_min : IM_U32_MIN, v_max ? *(const ImU32* )v_max : IM_U32_MAX, format, power, flags); + case ImGuiDataType_S64: return DragBehaviorT(data_type, (ImS64*)v, v_speed, v_min ? *(const ImS64* )v_min : IM_S64_MIN, v_max ? *(const ImS64* )v_max : IM_S64_MAX, format, power, flags); + case ImGuiDataType_U64: return DragBehaviorT(data_type, (ImU64*)v, v_speed, v_min ? *(const ImU64* )v_min : IM_U64_MIN, v_max ? *(const ImU64* )v_max : IM_U64_MAX, format, power, flags); + case ImGuiDataType_Float: return DragBehaviorT(data_type, (float*)v, v_speed, v_min ? *(const float* )v_min : -FLT_MAX, v_max ? *(const float* )v_max : FLT_MAX, format, power, flags); + case ImGuiDataType_Double: return DragBehaviorT(data_type, (double*)v, v_speed, v_min ? *(const double*)v_min : -DBL_MAX, v_max ? *(const double*)v_max : DBL_MAX, format, power, flags); case ImGuiDataType_COUNT: break; } IM_ASSERT(0); @@ -1701,11 +1887,14 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* v, floa } } if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id)) + { + FocusableItemUnregister(window); return InputScalarAsWidgetReplacement(frame_bb, id, label, data_type, v, format); + } // Actual drag behavior ItemSize(total_bb, style.FramePadding.y); - const bool value_changed = DragBehavior(id, data_type, v, v_speed, v_min, v_max, format, power); + const bool value_changed = DragBehavior(id, data_type, v, v_speed, v_min, v_max, format, power, ImGuiDragFlags_None); if (value_changed) MarkItemEdited(id); @@ -1844,7 +2033,8 @@ bool ImGui::DragIntRange2(const char* label, int* v_current_min, int* v_current_ } //------------------------------------------------------------------------- -// WIDGETS: Sliders +// [SECTION] Widgets: SliderScalar, SliderFloat, SliderInt, etc. +//------------------------------------------------------------------------- // - SliderBehaviorT<>() [Internal] // - SliderBehavior() [Internal] // - SliderScalar() @@ -1864,7 +2054,7 @@ bool ImGui::DragIntRange2(const char* label, int* v_current_min, int* v_current_ //------------------------------------------------------------------------- template -static inline float SliderBehaviorCalcRatioFromValue(ImGuiDataType data_type, TYPE v, TYPE v_min, TYPE v_max, float power, float linear_zero_pos) +float ImGui::SliderCalcRatioFromValueT(ImGuiDataType data_type, TYPE v, TYPE v_min, TYPE v_max, float power, float linear_zero_pos) { if (v_min == v_max) return 0.0f; @@ -1891,25 +2081,25 @@ static inline float SliderBehaviorCalcRatioFromValue(ImGuiDataType data_type, TY // FIXME: Move some of the code into SliderBehavior(). Current responsability is larger than what the equivalent DragBehaviorT<> does, we also do some rendering, etc. template -static bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, TYPE* v, const TYPE v_min, const TYPE v_max, const char* format, float power, ImGuiSliderFlags flags, ImRect* out_grab_bb) +bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, TYPE* v, const TYPE v_min, const TYPE v_max, const char* format, float power, ImGuiSliderFlags flags, ImRect* out_grab_bb) { ImGuiContext& g = *GImGui; const ImGuiStyle& style = g.Style; - const bool is_horizontal = (flags & ImGuiSliderFlags_Vertical) == 0; + const ImGuiAxis axis = (flags & ImGuiSliderFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X; const bool is_decimal = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double); const bool is_power = (power != 1.0f) && is_decimal; const float grab_padding = 2.0f; - const float slider_sz = is_horizontal ? (bb.GetWidth() - grab_padding * 2.0f) : (bb.GetHeight() - grab_padding * 2.0f); + const float slider_sz = (bb.Max[axis] - bb.Min[axis]) - grab_padding * 2.0f; float grab_sz = style.GrabMinSize; SIGNEDTYPE v_range = (v_min < v_max ? v_max - v_min : v_min - v_max); if (!is_decimal && v_range >= 0) // v_range < 0 may happen on integer overflows grab_sz = ImMax((float)(slider_sz / (v_range + 1)), style.GrabMinSize); // For integer sliders: if possible have the grab size represent 1 unit grab_sz = ImMin(grab_sz, slider_sz); const float slider_usable_sz = slider_sz - grab_sz; - const float slider_usable_pos_min = (is_horizontal ? bb.Min.x : bb.Min.y) + grab_padding + grab_sz*0.5f; - const float slider_usable_pos_max = (is_horizontal ? bb.Max.x : bb.Max.y) - grab_padding - grab_sz*0.5f; + const float slider_usable_pos_min = bb.Min[axis] + grab_padding + grab_sz*0.5f; + const float slider_usable_pos_max = bb.Max[axis] - grab_padding - grab_sz*0.5f; // For power curve sliders that cross over sign boundary we want the curve to be symmetric around 0.0f float linear_zero_pos; // 0.0->1.0f @@ -1940,9 +2130,9 @@ static bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType d } else { - const float mouse_abs_pos = is_horizontal ? g.IO.MousePos.x : g.IO.MousePos.y; + const float mouse_abs_pos = g.IO.MousePos[axis]; clicked_t = (slider_usable_sz > 0.0f) ? ImClamp((mouse_abs_pos - slider_usable_pos_min) / slider_usable_sz, 0.0f, 1.0f) : 0.0f; - if (!is_horizontal) + if (axis == ImGuiAxis_Y) clicked_t = 1.0f - clicked_t; set_new_value = true; } @@ -1950,14 +2140,14 @@ static bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType d else if (g.ActiveIdSource == ImGuiInputSource_Nav) { const ImVec2 delta2 = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard | ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_RepeatFast, 0.0f, 0.0f); - float delta = is_horizontal ? delta2.x : -delta2.y; + float delta = (axis == ImGuiAxis_X) ? delta2.x : -delta2.y; if (g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated) { ClearActiveID(); } else if (delta != 0.0f) { - clicked_t = SliderBehaviorCalcRatioFromValue(data_type, *v, v_min, v_max, power, linear_zero_pos); + clicked_t = SliderCalcRatioFromValueT(data_type, *v, v_min, v_max, power, linear_zero_pos); const int decimal_precision = is_decimal ? ImParseFormatPrecision(format, 3) : 0; if ((decimal_precision > 0) || is_power) { @@ -2029,7 +2219,7 @@ static bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType d } // Round to user desired precision based on format string - v_new = RoundScalarWithFormat(format, data_type, v_new); + v_new = RoundScalarWithFormatT(format, data_type, v_new); // Apply result if (*v != v_new) @@ -2041,11 +2231,11 @@ static bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType d } // Output grab position so it can be displayed by the caller - float grab_t = SliderBehaviorCalcRatioFromValue(data_type, *v, v_min, v_max, power, linear_zero_pos); - if (!is_horizontal) + float grab_t = SliderCalcRatioFromValueT(data_type, *v, v_min, v_max, power, linear_zero_pos); + if (axis == ImGuiAxis_Y) grab_t = 1.0f - grab_t; const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t); - if (is_horizontal) + if (axis == ImGuiAxis_X) *out_grab_bb = ImRect(grab_pos - grab_sz*0.5f, bb.Min.y + grab_padding, grab_pos + grab_sz*0.5f, bb.Max.y - grab_padding); else *out_grab_bb = ImRect(bb.Min.x + grab_padding, grab_pos - grab_sz*0.5f, bb.Max.x - grab_padding, grab_pos + grab_sz*0.5f); @@ -2131,7 +2321,10 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* v, co } } if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id)) + { + FocusableItemUnregister(window); return InputScalarAsWidgetReplacement(frame_bb, id, label, data_type, v, format); + } ItemSize(total_bb, style.FramePadding.y); @@ -2209,10 +2402,12 @@ bool ImGui::SliderFloat4(const char* label, float v[4], float v_min, float v_max return SliderScalarN(label, ImGuiDataType_Float, v, 4, &v_min, &v_max, format, power); } -bool ImGui::SliderAngle(const char* label, float* v_rad, float v_degrees_min, float v_degrees_max) +bool ImGui::SliderAngle(const char* label, float* v_rad, float v_degrees_min, float v_degrees_max, const char* format) { + if (format == NULL) + format = "%.0f deg"; float v_deg = (*v_rad) * 360.0f / (2*IM_PI); - bool value_changed = SliderFloat(label, &v_deg, v_degrees_min, v_degrees_max, "%.0f deg", 1.0f); + bool value_changed = SliderFloat(label, &v_deg, v_degrees_min, v_degrees_max, format, 1.0f); *v_rad = v_deg * (2*IM_PI) / 360.0f; return value_changed; } @@ -2308,11 +2503,12 @@ bool ImGui::VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, } //------------------------------------------------------------------------- -// WIDGETS: Inputs (_excepted InputText_) -// - ImParseFormatFindStart() -// - ImParseFormatFindEnd() -// - ImParseFormatTrimDecorations() -// - ImParseFormatPrecision() +// [SECTION] Widgets: InputScalar, InputFloat, InputInt, etc. +//------------------------------------------------------------------------- +// - ImParseFormatFindStart() [Internal] +// - ImParseFormatFindEnd() [Internal] +// - ImParseFormatTrimDecorations() [Internal] +// - ImParseFormatPrecision() [Internal] // - InputScalarAsWidgetReplacement() [Internal] // - InputScalar() // - InputScalarN() @@ -2399,8 +2595,8 @@ int ImParseFormatPrecision(const char* fmt, int default_precision) return (precision == INT_MAX) ? default_precision : precision; } -// Create text input in place of a slider (when CTRL+Clicking on slider) -// FIXME: Logic is messy and confusing. +// Create text input in place of an active drag/slider (used when doing a CTRL+Click on drag/slider widgets) +// FIXME: Logic is awkward and confusing. This should be reworked to facilitate using in other situations. bool ImGui::InputScalarAsWidgetReplacement(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* data_ptr, const char* format) { ImGuiContext& g = *GImGui; @@ -2409,9 +2605,8 @@ bool ImGui::InputScalarAsWidgetReplacement(const ImRect& bb, ImGuiID id, const c // Our replacement widget will override the focus ID (registered previously to allow for a TAB focus to happen) // On the first frame, g.ScalarAsInputTextId == 0, then on subsequent frames it becomes == id SetActiveID(g.ScalarAsInputTextId, window); - g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); SetHoveredID(0); - FocusableItemUnregister(window); + g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); char fmt_buf[32]; char data_buf[32]; @@ -2605,7 +2800,8 @@ bool ImGui::InputDouble(const char* label, double* v, double step, double step_f } //------------------------------------------------------------------------- -// WIDGETS: InputText +// [SECTION] Widgets: InputText, InputTextMultiline +//------------------------------------------------------------------------- // - InputText() // - InputTextMultiline() // - InputTextEx() [Internal] @@ -2661,7 +2857,7 @@ static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* t if (c == '\r') continue; - const float char_width = font->GetCharAdvance((unsigned short)c) * scale; + const float char_width = font->GetCharAdvance((ImWchar)c) * scale; line_width += char_width; } @@ -2722,7 +2918,7 @@ static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n) obj->CurLenA -= ImTextCountUtf8BytesFromStr(dst, dst + n); obj->CurLenW -= n; - // Offset remaining text + // Offset remaining text (FIXME-OPT: Use memmove) const ImWchar* src = obj->TextW.Data + pos + n; while (ImWchar c = *src++) *dst++ = c; @@ -2778,7 +2974,7 @@ static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const Im #define STB_TEXTEDIT_K_SHIFT 0x20000 #define STB_TEXTEDIT_IMPLEMENTATION -#include "stb_textedit.h" +#include "imstb_textedit.h" } @@ -2910,7 +3106,7 @@ static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags f // This is so we can easily call InputText() on static arrays using ARRAYSIZE() and to match // Note that in std::string world, capacity() would omit 1 byte used by the zero-terminator. // - When active, hold on a privately held copy of the text (and apply back to 'buf'). So changing 'buf' while the InputText is active has no effect. -// - If you want to use ImGui::InputText() with std::string, see misc/stl/imgui_stl.h +// - If you want to use ImGui::InputText() with std::string, see misc/cpp/imgui_stdlib.h // (FIXME: Rather messy function partly because we are doing UTF8 > u16 > UTF8 conversions on the go to more easily handle stb_textedit calls. Ideally we should stay in UTF-8 all the time. See https://github.com/nothings/stb/issues/188) bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* callback_user_data) { @@ -3220,7 +3416,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 { // Filter pasted buffer const int clipboard_len = (int)strlen(clipboard); - ImWchar* clipboard_filtered = (ImWchar*)ImGui::MemAlloc((clipboard_len+1) * sizeof(ImWchar)); + ImWchar* clipboard_filtered = (ImWchar*)MemAlloc((clipboard_len+1) * sizeof(ImWchar)); int clipboard_filtered_len = 0; for (const char* s = clipboard; *s; ) { @@ -3238,7 +3434,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 stb_textedit_paste(&edit_state, &edit_state.StbState, clipboard_filtered, clipboard_filtered_len); edit_state.CursorFollow = true; } - ImGui::MemFree(clipboard_filtered); + MemFree(clipboard_filtered); } } } @@ -3433,6 +3629,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 // In multi-line mode, we never exit the loop until all lines are counted, so add one extra to the searches_remaining counter. searches_remaining += is_multiline ? 1 : 0; int line_count = 0; + //for (const ImWchar* s = text_begin; (s = (const ImWchar*)wcschr((const wchar_t*)s, (wchar_t)'\n')) != NULL; s++) // FIXME-OPT: Could use this when wchar_t are 16-bits for (const ImWchar* s = text_begin; *s != 0; s++) if (*s == '\n') { @@ -3507,6 +3704,8 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 break; if (rect_pos.y < clip_rect.y) { + //p = (const ImWchar*)wmemchr((const wchar_t*)p, '\n', text_selected_end - p); // FIXME-OPT: Could use this when wchar_t are 16-bits + //p = p ? p + 1 : text_selected_end; while (p < text_selected_end) if (*p++ == '\n') break; @@ -3514,7 +3713,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 else { ImVec2 rect_size = InputTextCalcTextSizeW(p, text_selected_end, &p, NULL, true); - if (rect_size.x <= 0.0f) rect_size.x = (float)(int)(g.Font->GetCharAdvance((unsigned short)' ') * 0.50f); // So we can see selected empty lines + if (rect_size.x <= 0.0f) rect_size.x = (float)(int)(g.Font->GetCharAdvance((ImWchar)' ') * 0.50f); // So we can see selected empty lines ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos +ImVec2(rect_size.x, bg_offy_dn)); rect.ClipWith(clip_rect); if (rect.Overlaps(clip_rect)) @@ -3530,7 +3729,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos - render_scroll, GetColorU32(ImGuiCol_Text), buf_display, buf_display + buf_display_len, 0.0f, is_multiline ? NULL : &clip_rect); // Draw blinking cursor - bool cursor_is_visible = (!g.IO.ConfigCursorBlink) || (g.InputTextState.CursorAnim <= 0.0f) || ImFmod(g.InputTextState.CursorAnim, 1.20f) <= 0.80f; + bool cursor_is_visible = (!g.IO.ConfigInputTextCursorBlink) || (g.InputTextState.CursorAnim <= 0.0f) || ImFmod(g.InputTextState.CursorAnim, 1.20f) <= 0.80f; ImVec2 cursor_screen_pos = render_pos + cursor_offset - render_scroll; ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y-g.FontSize+0.5f, cursor_screen_pos.x+1.0f, cursor_screen_pos.y-1.5f); if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect)) @@ -3579,7 +3778,8 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 } //------------------------------------------------------------------------- -// WIDGETS: Color Editor / Picker +// [SECTION] Widgets: ColorEdit, ColorPicker, ColorButton, etc. +//------------------------------------------------------------------------- // - ColorEdit3() // - ColorEdit4() // - ColorPicker3() @@ -3738,7 +3938,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag if (label != label_display_end) { TextUnformatted(label, label_display_end); - Separator(); + Spacing(); } ImGuiColorEditFlags picker_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaBar; ImGuiColorEditFlags picker_flags = (flags_untouched & picker_flags_to_forward) | ImGuiColorEditFlags__InputsMask | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaPreviewHalf; @@ -4344,42 +4544,43 @@ void ImGui::ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags fl { bool allow_opt_picker = !(flags & ImGuiColorEditFlags__PickerMask); bool allow_opt_alpha_bar = !(flags & ImGuiColorEditFlags_NoAlpha) && !(flags & ImGuiColorEditFlags_AlphaBar); - if ((!allow_opt_picker && !allow_opt_alpha_bar) || !ImGui::BeginPopup("context")) + if ((!allow_opt_picker && !allow_opt_alpha_bar) || !BeginPopup("context")) return; ImGuiContext& g = *GImGui; if (allow_opt_picker) { - ImVec2 picker_size(g.FontSize * 8, ImMax(g.FontSize * 8 - (ImGui::GetFrameHeight() + g.Style.ItemInnerSpacing.x), 1.0f)); // FIXME: Picker size copied from main picker function - ImGui::PushItemWidth(picker_size.x); + ImVec2 picker_size(g.FontSize * 8, ImMax(g.FontSize * 8 - (GetFrameHeight() + g.Style.ItemInnerSpacing.x), 1.0f)); // FIXME: Picker size copied from main picker function + PushItemWidth(picker_size.x); for (int picker_type = 0; picker_type < 2; picker_type++) { // Draw small/thumbnail version of each picker type (over an invisible button for selection) - if (picker_type > 0) ImGui::Separator(); - ImGui::PushID(picker_type); + if (picker_type > 0) Separator(); + PushID(picker_type); ImGuiColorEditFlags picker_flags = ImGuiColorEditFlags_NoInputs|ImGuiColorEditFlags_NoOptions|ImGuiColorEditFlags_NoLabel|ImGuiColorEditFlags_NoSidePreview|(flags & ImGuiColorEditFlags_NoAlpha); if (picker_type == 0) picker_flags |= ImGuiColorEditFlags_PickerHueBar; if (picker_type == 1) picker_flags |= ImGuiColorEditFlags_PickerHueWheel; - ImVec2 backup_pos = ImGui::GetCursorScreenPos(); - if (ImGui::Selectable("##selectable", false, 0, picker_size)) // By default, Selectable() is closing popup + ImVec2 backup_pos = GetCursorScreenPos(); + if (Selectable("##selectable", false, 0, picker_size)) // By default, Selectable() is closing popup g.ColorEditOptions = (g.ColorEditOptions & ~ImGuiColorEditFlags__PickerMask) | (picker_flags & ImGuiColorEditFlags__PickerMask); - ImGui::SetCursorScreenPos(backup_pos); + SetCursorScreenPos(backup_pos); ImVec4 dummy_ref_col; memcpy(&dummy_ref_col.x, ref_col, sizeof(float) * (picker_flags & ImGuiColorEditFlags_NoAlpha ? 3 : 4)); - ImGui::ColorPicker4("##dummypicker", &dummy_ref_col.x, picker_flags); - ImGui::PopID(); + ColorPicker4("##dummypicker", &dummy_ref_col.x, picker_flags); + PopID(); } - ImGui::PopItemWidth(); + PopItemWidth(); } if (allow_opt_alpha_bar) { - if (allow_opt_picker) ImGui::Separator(); - ImGui::CheckboxFlags("Alpha Bar", (unsigned int*)&g.ColorEditOptions, ImGuiColorEditFlags_AlphaBar); + if (allow_opt_picker) Separator(); + CheckboxFlags("Alpha Bar", (unsigned int*)&g.ColorEditOptions, ImGuiColorEditFlags_AlphaBar); } - ImGui::EndPopup(); + EndPopup(); } //------------------------------------------------------------------------- -// WIDGETS: Trees +// [SECTION] Widgets: TreeNode, CollapsingHeader, etc. +//------------------------------------------------------------------------- // - TreeNode() // - TreeNodeV() // - TreeNodeEx() @@ -4772,11 +4973,12 @@ bool ImGui::CollapsingHeader(const char* label, bool* p_open, ImGuiTreeNodeFlags } //------------------------------------------------------------------------- -// WIDGETS: Selectables +// [SECTION] Widgets: Selectable +//------------------------------------------------------------------------- // - Selectable() //------------------------------------------------------------------------- -// Tip: pass an empty label (e.g. "##dummy") then you can use the space to draw other text or image. +// Tip: pass a non-visible label (e.g. "##dummy") then you can use the space to draw other text or image. // But you need to make sure the ID is unique, e.g. enclose calls in PushID/PopID or use ##unique_id. bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags flags, const ImVec2& size_arg) { @@ -4880,15 +5082,16 @@ bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags } //------------------------------------------------------------------------- -// WIDGETS: List Box +// [SECTION] Widgets: ListBox +//------------------------------------------------------------------------- // - ListBox() // - ListBoxHeader() // - ListBoxFooter() //------------------------------------------------------------------------- -// FIXME: Rename to BeginListBox() +// FIXME: In principle this function should be called BeginListBox(). We should rename it after re-evaluating if we want to keep the same signature. // Helper to calculate the size of a listbox and display a label on the right. -// Tip: To have a list filling the entire window width, PushItemWidth(-1) and pass an empty label "##empty" +// Tip: To have a list filling the entire window width, PushItemWidth(-1) and pass an non-visible label e.g. "##empty" bool ImGui::ListBoxHeader(const char* label, const ImVec2& size_arg) { ImGuiWindow* window = GetCurrentWindow(); @@ -4914,24 +5117,26 @@ bool ImGui::ListBoxHeader(const char* label, const ImVec2& size_arg) return true; } -// FIXME: Rename to BeginListBox() +// FIXME: In principle this function should be called EndListBox(). We should rename it after re-evaluating if we want to keep the same signature. bool ImGui::ListBoxHeader(const char* label, int items_count, int height_in_items) { - // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar. - // We don't add +0.40f if items_count <= height_in_items. It is slightly dodgy, because it means a dynamic list of items will make the widget resize occasionally when it crosses that size. + // Size default to hold ~7.25 items. + // We add +25% worth of item height to allow the user to see at a glance if there are more items up/down, without looking at the scrollbar. + // We don't add this extra bit if items_count <= height_in_items. It is slightly dodgy, because it means a dynamic list of items will make the widget resize occasionally when it crosses that size. // I am expecting that someone will come and complain about this behavior in a remote future, then we can advise on a better solution. if (height_in_items < 0) height_in_items = ImMin(items_count, 7); - float height_in_items_f = height_in_items < items_count ? (height_in_items + 0.40f) : (height_in_items + 0.00f); + const ImGuiStyle& style = GetStyle(); + float height_in_items_f = (height_in_items < items_count) ? (height_in_items + 0.25f) : (height_in_items + 0.00f); // We include ItemSpacing.y so that a list sized for the exact number of items doesn't make a scrollbar appears. We could also enforce that by passing a flag to BeginChild(). ImVec2 size; size.x = 0.0f; - size.y = GetTextLineHeightWithSpacing() * height_in_items_f + GetStyle().ItemSpacing.y; + size.y = GetTextLineHeightWithSpacing() * height_in_items_f + style.FramePadding.y * 2.0f; return ListBoxHeader(label, size); } -// FIXME: Rename to EndListBox() +// FIXME: In principle this function should be called EndListBox(). We should rename it after re-evaluating if we want to keep the same signature. void ImGui::ListBoxFooter() { ImGuiWindow* parent_window = GetCurrentWindow()->ParentWindow; @@ -4989,7 +5194,8 @@ bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(v } //------------------------------------------------------------------------- -// WIDGETS: Data Plotting +// [SECTION] Widgets: PlotLines, PlotHistogram +//------------------------------------------------------------------------- // - PlotEx() [Internal] // - PlotLines() // - PlotHistogram() @@ -5143,7 +5349,9 @@ void ImGui::PlotHistogram(const char* label, float (*values_getter)(void* data, } //------------------------------------------------------------------------- -// WIDGETS: Value() helpers +// [SECTION] Widgets: Value helpers +// Those is not very useful, legacy API. +//------------------------------------------------------------------------- // - Value() //------------------------------------------------------------------------- @@ -5177,8 +5385,9 @@ void ImGui::Value(const char* prefix, float v, const char* float_format) } //------------------------------------------------------------------------- -// WIDGETS: Menus -// - ImGuiMenuColumns +// [SECTION] MenuItem, BeginMenu, EndMenu, etc. +//------------------------------------------------------------------------- +// - ImGuiMenuColumns [Internal] // - BeginMainMenuBar() // - EndMainMenuBar() // - BeginMenuBar() @@ -5258,7 +5467,7 @@ void ImGui::EndMainMenuBar() // When the user has left the menu layer (typically: closed menus through activation of an item), we restore focus to the previous window ImGuiContext& g = *GImGui; if (g.CurrentWindow == g.NavWindow && g.NavLayer == 0) - FocusFrontMostActiveWindowIgnoringOne(g.NavWindow); + FocusPreviousWindowIgnoringOne(g.NavWindow); End(); } @@ -5357,7 +5566,7 @@ bool ImGui::BeginMenu(const char* label, bool enabled) // Menu inside an horizontal menu bar // Selectable extend their highlight by half ItemSpacing in each direction. // For ChildMenu, the popup position will be overwritten by the call to FindBestWindowPosForPopup() in Begin() - popup_pos = ImVec2(pos.x - window->WindowPadding.x, pos.y - style.FramePadding.y + window->MenuBarHeight()); + popup_pos = ImVec2(pos.x - 1.0f - (float)(int)(style.ItemSpacing.x * 0.5f), pos.y - style.FramePadding.y + window->MenuBarHeight()); window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f); PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing * 2.0f); float w = label_size.x; @@ -5533,4 +5742,3 @@ bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected, } return false; } - diff --git a/src/lib/geogram_gfx/third_party/ImGui/OLD/stb_rect_pack.h b/src/lib/geogram_gfx/third_party/ImGui/OLD/imstb_rectpack.h similarity index 100% rename from src/lib/geogram_gfx/third_party/ImGui/OLD/stb_rect_pack.h rename to src/lib/geogram_gfx/third_party/ImGui/OLD/imstb_rectpack.h diff --git a/src/lib/geogram_gfx/third_party/ImGui/OLD/stb_textedit.h b/src/lib/geogram_gfx/third_party/ImGui/OLD/imstb_textedit.h similarity index 100% rename from src/lib/geogram_gfx/third_party/ImGui/OLD/stb_textedit.h rename to src/lib/geogram_gfx/third_party/ImGui/OLD/imstb_textedit.h diff --git a/src/lib/geogram_gfx/third_party/ImGui/OLD/stb_truetype.h b/src/lib/geogram_gfx/third_party/ImGui/OLD/imstb_truetype.h similarity index 100% rename from src/lib/geogram_gfx/third_party/ImGui/OLD/stb_truetype.h rename to src/lib/geogram_gfx/third_party/ImGui/OLD/imstb_truetype.h diff --git a/src/lib/geogram_gfx/third_party/ImGui/README.txt b/src/lib/geogram_gfx/third_party/ImGui/README.txt index 820536f5..41c93305 100644 --- a/src/lib/geogram_gfx/third_party/ImGui/README.txt +++ b/src/lib/geogram_gfx/third_party/ImGui/README.txt @@ -1,6 +1,6 @@ imgui: Bloat-free Immediate Mode Graphical User interface for C++ with minimal dependencies website: https://github.com/ocornut/imgui -This version (1.66) downloaded 11/24/2018 +This version (1.69) downloaded 03/16/2019 My patches/changes indicated by [Bruno Levy] tags Added Android support. \ No newline at end of file diff --git a/src/lib/geogram_gfx/third_party/ImGui/imgui.cpp b/src/lib/geogram_gfx/third_party/ImGui/imgui.cpp index 25b71de4..a1020234 100644 --- a/src/lib/geogram_gfx/third_party/ImGui/imgui.cpp +++ b/src/lib/geogram_gfx/third_party/ImGui/imgui.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.66 +// dear imgui, v1.69 // (main code and documentation) // Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp for demo code. @@ -7,9 +7,12 @@ // Releases change-log at https://github.com/ocornut/imgui/releases // Technical Support for Getting Started https://discourse.dearimgui.org/c/getting-started // Gallery (please post your screenshots/video there!): https://github.com/ocornut/imgui/issues/1269 + // Developed by Omar Cornut and every direct or indirect contributors to the GitHub. +// See LICENSE.txt for copyright and licensing details (standard MIT License). // This library is free but I need your support to sustain development and maintenance. -// If you work for a company, please consider financial support, see README. For individuals: https://www.patreon.com/imgui +// Businesses: you can support continued maintenance and development via support contracts or sponsoring, see docs/README. +// Individuals: you can support continued maintenance and development via donations or Patreon https://www.patreon.com/imgui. // It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library. // Note that 'ImGui::' being a namespace, you can add functions into the namespace from your own source files, without @@ -26,36 +29,38 @@ DOCUMENTATION - MISSION STATEMENT - END-USER GUIDE - PROGRAMMER GUIDE (read me!) - - Read first - - How to update to a newer version of Dear ImGui - - Getting started with integrating Dear ImGui in your code/engine - - This is how a simple application may look like (2 variations) - - This is how a simple rendering function may look like - - Using gamepad/keyboard navigation controls + - Read first. + - How to update to a newer version of Dear ImGui. + - Getting started with integrating Dear ImGui in your code/engine. + - This is how a simple application may look like (2 variations). + - This is how a simple rendering function may look like. + - Using gamepad/keyboard navigation controls. - API BREAKING CHANGES (read me when you update!) - FREQUENTLY ASKED QUESTIONS (FAQ), TIPS - How can I tell whether to dispatch mouse/keyboard to imgui or to my application? - How can I display an image? What is ImTextureID, how does it works? - - How can I have multiple widgets with the same label or without a label? A primer on labels and the ID Stack. - - How can I use my own math types instead of ImVec2/ImVec4? + - How can I have multiple widgets with the same label or with an empty label? A primer on labels and the ID Stack. + - How can I use my own math types instead of ImVec2/ImVec4? - How can I load a different font than the default? - How can I easily use icons in my application? - How can I load multiple fonts? - How can I display and input non-latin characters such as Chinese, Japanese, Korean, Cyrillic? + - How can I interact with standard C++ types (such as std::string and std::vector)? - How can I use the drawing facilities without an ImGui window? (using ImDrawList API) + - How can I use Dear ImGui on a platform that doesn't have a mouse or a keyboard? (input share, remoting, gamepad) - I integrated Dear ImGui in my engine and the text or lines are blurry.. - I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around.. - How can I help? -CODE +CODE (search for "[SECTION]" in the code to find them) // [SECTION] FORWARD DECLARATIONS // [SECTION] CONTEXT AND MEMORY ALLOCATORS // [SECTION] MAIN USER FACING STRUCTURES (ImGuiStyle, ImGuiIO) -// [SECTION] MISC HELPER/UTILITIES (Maths, String, Format, Hash, File functions) -// [SECTION] MISC HELPER/UTILITIES (ImText* functions) -// [SECTION] MISC HELPER/UTILITIES (Color functions) +// [SECTION] MISC HELPERS/UTILITIES (Maths, String, Format, Hash, File functions) +// [SECTION] MISC HELPERS/UTILITIES (ImText* functions) +// [SECTION] MISC HELPERS/UTILITIES (Color functions) // [SECTION] ImGuiStorage // [SECTION] ImGuiTextFilter // [SECTION] ImGuiTextBuffer @@ -83,19 +88,19 @@ CODE MISSION STATEMENT ================= - - Easy to use to create code-driven and data-driven tools - - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools - - Easy to hack and improve - - Minimize screen real-estate usage - - Minimize setup and maintenance - - Minimize state storage on user side - - Portable, minimize dependencies, run on target (consoles, phones, etc.) - - Efficient runtime and memory consumption (NB- we do allocate when "growing" content e.g. creating a window, - opening a tree node for the first time, etc. but a typical frame should not allocate anything) + - Easy to use to create code-driven and data-driven tools. + - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools. + - Easy to hack and improve. + - Minimize screen real-estate usage. + - Minimize setup and maintenance. + - Minimize state storage on user side. + - Portable, minimize dependencies, run on target (consoles, phones, etc.). + - Efficient runtime and memory consumption (NB- we do allocate when "growing" content e.g. creating a window,. + opening a tree node for the first time, etc. but a typical frame should not allocate anything). Designed for developers and content-creators, not the typical end-user! Some of the weaknesses includes: - - Doesn't look fancy, doesn't animate - - Limited layout features, intricate layouts are typically crafted in code + - Doesn't look fancy, doesn't animate. + - Limited layout features, intricate layouts are typically crafted in code. END-USER GUIDE @@ -125,16 +130,29 @@ CODE PROGRAMMER GUIDE ================ - READ FIRST + READ FIRST: - Read the FAQ below this section! - Your code creates the UI, if your code doesn't run the UI is gone! The UI can be highly dynamic, there are no construction or destruction steps, less superfluous data retention on your side, less state duplication, less state synchronization, less bugs. - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features. - - You can learn about immediate-mode GUI principles at http://www.johno.se/book/imgui.html or watch http://mollyrocket.com/861 - See README.md for more links describing the IMGUI paradigm. Dear ImGui is an implementation of the IMGUI paradigm. - - HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI + - The library is designed to be built from sources. Avoid pre-compiled binaries and packaged versions. See imconfig.h to configure your build. + - Dear ImGui is an implementation of the IMGUI paradigm (immediate-mode graphical user interface, a term coined by Casey Muratori). + You can learn about IMGUI principles at http://www.johno.se/book/imgui.html, http://mollyrocket.com/861 & more links docs/README.md. + - Dear ImGui is a "single pass" rasterizing implementation of the IMGUI paradigm, aimed at ease of use and high-performances. + For every application frame your UI code will be called only once. This is in contrast to e.g. Unity's own implementation of an IMGUI, + where the UI code is called multiple times ("multiple passes") from a single entry point. There are pros and cons to both approaches. + - Our origin are on the top-left. In axis aligned bounding boxes, Min = top-left, Max = bottom-right. + - This codebase is also optimized to yield decent performances with typical "Debug" builds settings. + - Please make sure you have asserts enabled (IM_ASSERT redirects to assert() by default, but can be redirected). + If you get an assert, read the messages and comments around the assert. + - C++: this is a very C-ish codebase: we don't rely on C++11, we don't include any C++ headers, and ImGui:: is a namespace. + - C++: ImVec2/ImVec4 do not expose math operators by default, because it is expected that you use your own math types. + See FAQ "How can I use my own math types instead of ImVec2/ImVec4?" for details about setting up imconfig.h for that. + However, imgui_internal.h can optionally export math operators for ImVec2/ImVec4, which we use in this codebase. + - C++: pay attention that ImVector<> manipulates plain-old-data and does not honor construction/destruction (avoid using it in your code!). + + HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI: - Overwrite all the sources files except for imconfig.h (if you have made modification to your copy of imconfig.h) - Or maintain your own branch where you have imconfig.h modified. @@ -144,7 +162,7 @@ CODE likely be a comment about it. Please report any issue to the GitHub page! - Try to keep your copy of dear imgui reasonably up to date. - GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE + GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE: - Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library. - Add the Dear ImGui source files to your projects or using your preferred build system. @@ -157,8 +175,8 @@ CODE - Refer to the bindings and demo applications in the examples/ folder for instruction on how to setup your code. - If you are running over a standard OS with a common graphics API, you should be able to use unmodified imgui_impl_*** files from the examples/ folder. - HOW A SIMPLE APPLICATION MAY LOOK LIKE - EXHIBIT 1: USING THE EXAMPLE BINDINGS (imgui_impl_XXX.cpp files from the examples/ folder) + HOW A SIMPLE APPLICATION MAY LOOK LIKE: + EXHIBIT 1: USING THE EXAMPLE BINDINGS (imgui_impl_XXX.cpp files from the examples/ folder). // Application init: create a dear imgui context, setup some options, load fonts ImGui::CreateContext(); @@ -187,14 +205,14 @@ CODE ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); g_pSwapChain->Present(1, 0); } - + // Shutdown ImGui_ImplDX11_Shutdown(); ImGui_ImplWin32_Shutdown(); ImGui::DestroyContext(); - HOW A SIMPLE APPLICATION MAY LOOK LIKE - EXHIBIT 2: IMPLEMENTING CUSTOM BINDING / CUSTOM ENGINE + HOW A SIMPLE APPLICATION MAY LOOK LIKE: + EXHIBIT 2: IMPLEMENTING CUSTOM BINDING / CUSTOM ENGINE. // Application init: create a dear imgui context, setup some options, load fonts ImGui::CreateContext(); @@ -248,16 +266,17 @@ CODE // Shutdown ImGui::DestroyContext(); - HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE + HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE: void void MyImGuiRenderFunction(ImDrawData* draw_data) { // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled - // TODO: Setup viewport using draw_data->DisplaySize + // TODO: Setup viewport covering draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize // TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color. for (int n = 0; n < draw_data->CmdListsCount; n++) { + const ImDrawList* cmd_list = draw_data->CmdLists[n]; const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; // vertex buffer generated by ImGui const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; // index buffer generated by ImGui for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) @@ -278,7 +297,7 @@ CODE // (some elements visible outside their bounds) but you can fix that once everything else works! // - Clipping coordinates are provided in imgui coordinates space (from draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize) // In a single viewport application, draw_data->DisplayPos will always be (0,0) and draw_data->DisplaySize will always be == io.DisplaySize. - // However, in the interest of supporting multi-viewport applications in the future (see 'viewport' branch on github), + // However, in the interest of supporting multi-viewport applications in the future (see 'viewport' branch on github), // always subtract draw_data->DisplayPos from clipping bounds to convert them to your viewport space. // - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min) ImVec2 pos = draw_data->DisplayPos; @@ -295,13 +314,13 @@ CODE - The examples/ folders contains many actual implementation of the pseudo-codes above. - When calling NewFrame(), the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags are updated. - They tell you if Dear ImGui intends to use your inputs. When a flag is set you want to hide the corresponding inputs + They tell you if Dear ImGui intends to use your inputs. When a flag is set you want to hide the corresponding inputs from the rest of your application. In every cases you need to pass on the inputs to imgui. Refer to the FAQ for more information. - Please read the FAQ below!. Amusingly, it is called a FAQ because people frequently run into the same issues! USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS - - The gamepad/keyboard navigation is fairly functional and keeps being improved. + - The gamepad/keyboard navigation is fairly functional and keeps being improved. - Gamepad support is particularly useful to use dear imgui on a console system (e.g. PS4, Switch, XB1) without a mouse! - You can ask questions and report issues at https://github.com/ocornut/imgui/issues/787 - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable. @@ -345,9 +364,17 @@ CODE When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. You can read releases logs https://github.com/ocornut/imgui/releases for more details. - - 2018/10/12 (1.66) - Renamed misc/stl/imgui_stl.* to misc/cpp/imgui_stdlib.* in prevision for other C++ helper files. + - 2019/03/04 (1.69) - renamed GetOverlayDrawList() to GetForegroundDrawList(). Kept redirection function (will obsolete). + - 2019/02/26 (1.69) - renamed ImGuiColorEditFlags_RGB/ImGuiColorEditFlags_HSV/ImGuiColorEditFlags_HEX to ImGuiColorEditFlags_DisplayRGB/ImGuiColorEditFlags_DisplayHSV/ImGuiColorEditFlags_DisplayHex. Kept redirection enums (will obsolete). + - 2019/02/14 (1.68) - made it illegal/assert when io.DisplayTime == 0.0f (with an exception for the first frame). If for some reason your time step calculation gives you a zero value, replace it with a dummy small value! + - 2019/02/01 (1.68) - removed io.DisplayVisibleMin/DisplayVisibleMax (which were marked obsolete and removed from viewport/docking branch already). + - 2019/01/06 (1.67) - renamed io.InputCharacters[], marked internal as was always intended. Please don't access directly, and use AddInputCharacter() instead! + - 2019/01/06 (1.67) - renamed ImFontAtlas::GlyphRangesBuilder to ImFontGlyphRangesBuilder. Keep redirection typedef (will obsolete). + - 2018/12/20 (1.67) - made it illegal to call Begin("") with an empty string. This somehow half-worked before but had various undesirable side-effects. + - 2018/12/10 (1.67) - renamed io.ConfigResizeWindowsFromEdges to io.ConfigWindowsResizeFromEdges as we are doing a large pass on configuration flags. + - 2018/10/12 (1.66) - renamed misc/stl/imgui_stl.* to misc/cpp/imgui_stdlib.* in prevision for other C++ helper files. - 2018/09/28 (1.66) - renamed SetScrollHere() to SetScrollHereY(). Kept redirection function (will obsolete). - - 2018/09/06 (1.65) - renamed stb_truetype.h to imstb_truetype.h, stb_textedit.h to imstb_textedit.h, and stb_rect_pack.h to imstb_rectpack.h. + - 2018/09/06 (1.65) - renamed stb_truetype.h to imstb_truetype.h, stb_textedit.h to imstb_textedit.h, and stb_rect_pack.h to imstb_rectpack.h. If you were conveniently using the imgui copy of those STB headers in your project you will have to update your include paths. - 2018/09/05 (1.65) - renamed io.OptCursorBlink/io.ConfigCursorBlink to io.ConfigInputTextCursorBlink. (#1427) - 2018/08/31 (1.64) - added imgui_widgets.cpp file, extracted and moved widgets code out of imgui.cpp into imgui_widgets.cpp. Re-ordered some of the code remaining in imgui.cpp. @@ -356,12 +383,15 @@ CODE - 2018/08/22 (1.63) - renamed IsItemDeactivatedAfterChange() to IsItemDeactivatedAfterEdit() for consistency with new IsItemEdited() API. Kept redirection function (will obsolete soonish as IsItemDeactivatedAfterChange() is very recent). - 2018/08/21 (1.63) - renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. Kept redirection types (will obsolete). - 2018/08/21 (1.63) - removed ImGuiInputTextCallbackData::ReadOnly since it is a duplication of (ImGuiInputTextCallbackData::Flags & ImGuiInputTextFlags_ReadOnly). - - 2018/08/01 (1.63) - removed per-window ImGuiWindowFlags_ResizeFromAnySide beta flag in favor of a global io.ConfigResizeWindowsFromEdges to enable the feature. + - 2018/08/01 (1.63) - removed per-window ImGuiWindowFlags_ResizeFromAnySide beta flag in favor of a global io.ConfigResizeWindowsFromEdges [update 1.67 renamed to ConfigWindowsResizeFromEdges] to enable the feature. - 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink [-> io.ConfigInputTextCursorBlink in 1.65], io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency. - 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time. - 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete). + - 2018/06/08 (1.62) - examples: the imgui_impl_xxx files have been split to separate platform (Win32, Glfw, SDL2, etc.) from renderer (DX11, OpenGL, Vulkan, etc.). + old binding will still work as is, however prefer using the separated bindings as they will be updated to be multi-viewport conformant. + when adopting new bindings follow the main.cpp code of your preferred examples/ folder to know which functions to call. - 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set. - - 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details. + - 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details. - 2018/05/03 (1.61) - DragInt(): the default compile-time format string has been changed from "%.0f" to "%d", as we are not using integers internally any more. If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format. To honor backward-compatibility, the DragInt() code will currently parse and modify format strings to replace %*f with %d, giving time to users to upgrade their code. @@ -408,6 +438,7 @@ CODE removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting. - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead! - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete). + - 2017/09/26 (1.52) - renamed ImFont::Glyph to ImFontGlyph. Keep redirection typedef (will obsolete). - 2017/09/25 (1.52) - removed SetNextWindowPosCenter() because SetNextWindowPos() now has the optional pivot information to do the same and more. Kept redirection function (will obsolete). - 2017/08/25 (1.52) - io.MousePos needs to be set to ImVec2(-FLT_MAX,-FLT_MAX) when mouse is unavailable/missing. Previously ImVec2(-1,-1) was enough but we now accept negative mouse coordinates. In your binding if you need to support unavailable mouse, make sure to replace "io.MousePos = ImVec2(-1,-1)" with "io.MousePos = ImVec2(-FLT_MAX,-FLT_MAX)". - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)! @@ -465,7 +496,7 @@ CODE - the signature of the io.RenderDrawListsFn handler has changed! old: ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count) new: ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data). - argument: 'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount' + parameters: 'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount' ImDrawList: 'commands' becomes 'CmdBuffer', 'vtx_buffer' becomes 'VtxBuffer', 'IdxBuffer' is new. ImDrawCmd: 'vtx_count' becomes 'ElemCount', 'clip_rect' becomes 'ClipRect', 'user_callback' becomes 'UserCallback', 'texture_id' becomes 'TextureId'. - each ImDrawList now contains both a vertex buffer and an index buffer. For each command, render ElemCount/3 triangles using indices from the index buffer. @@ -498,9 +529,9 @@ CODE - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions. - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader. (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels. - font init: const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); <..Upload texture to GPU..> - became: unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); <..Upload texture to GPU>; io.Fonts->TexId = YourTextureIdentifier; - you now more flexibility to load multiple TTF fonts and manage the texture buffer for internal needs. + font init: { const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); <..Upload texture to GPU..>; } + became: { unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); <..Upload texture to GPU>; io.Fonts->TexId = YourTextureIdentifier; } + you now have more flexibility to load multiple TTF fonts and manage the texture buffer for internal needs. it is now recommended that you sample the font texture with bilinear interpolation. (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to set io.Fonts->TexID. (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix) @@ -540,7 +571,7 @@ CODE A: Short explanation: - You may use functions such as ImGui::Image(), ImGui::ImageButton() or lower-level ImDrawList::AddImage() to emit draw calls that will use your own textures. - Actual textures are identified in a way that is up to the user/engine. Those identifiers are stored and passed as ImTextureID (void*) value. - - Loading image files from the disk and turning them into a texture is not within the scope of Dear ImGui (for a good reason). + - Loading image files from the disk and turning them into a texture is not within the scope of Dear ImGui (for a good reason). Please read documentations or tutorials on your graphics API to understand how to display textures on the screen before moving onward. Long explanation: @@ -548,10 +579,10 @@ CODE At the end of the frame those meshes (ImDrawList) will be displayed by your rendering function. They are made up of textured polygons and the code to render them is generally fairly short (a few dozen lines). In the examples/ folder we provide functions for popular graphics API (OpenGL, DirectX, etc.). - Each rendering function decides on a data type to represent "textures". The concept of what is a "texture" is entirely tied to your underlying engine/graphics API. - We carry the information to identify a "texture" in the ImTextureID type. + We carry the information to identify a "texture" in the ImTextureID type. ImTextureID is nothing more that a void*, aka 4/8 bytes worth of data: just enough to store 1 pointer or 1 integer of your choice. Dear ImGui doesn't know or understand what you are storing in ImTextureID, it merely pass ImTextureID values until they reach your rendering function. - - In the examples/ bindings, for each graphics API binding we decided on a type that is likely to be a good representation for specifying + - In the examples/ bindings, for each graphics API binding we decided on a type that is likely to be a good representation for specifying an image from the end-user perspective. This is what the _examples_ rendering functions are using: OpenGL: ImTextureID = GLuint (see ImGui_ImplGlfwGL3_RenderDrawData() function in imgui_impl_glfw_gl3.cpp) @@ -559,13 +590,13 @@ CODE DirectX11: ImTextureID = ID3D11ShaderResourceView* (see ImGui_ImplDX11_RenderDrawData() function in imgui_impl_dx11.cpp) DirectX12: ImTextureID = D3D12_GPU_DESCRIPTOR_HANDLE (see ImGui_ImplDX12_RenderDrawData() function in imgui_impl_dx12.cpp) - For example, in the OpenGL example binding we store raw OpenGL texture identifier (GLuint) inside ImTextureID. - Whereas in the DirectX11 example binding we store a pointer to ID3D11ShaderResourceView inside ImTextureID, which is a higher-level structure + For example, in the OpenGL example binding we store raw OpenGL texture identifier (GLuint) inside ImTextureID. + Whereas in the DirectX11 example binding we store a pointer to ID3D11ShaderResourceView inside ImTextureID, which is a higher-level structure tying together both the texture and information about its format and how to read it. - If you have a custom engine built over e.g. OpenGL, instead of passing GLuint around you may decide to use a high-level data type to carry information about the texture as well as how to display it (shaders, etc.). The decision of what to use as ImTextureID can always be made better knowing how your codebase is designed. If your engine has high-level data types for "textures" and "material" then you may want to use them. - If you are starting with OpenGL or DirectX or Vulkan and haven't built much of a rendering engine over them, keeping the default ImTextureID + If you are starting with OpenGL or DirectX or Vulkan and haven't built much of a rendering engine over them, keeping the default ImTextureID representation suggested by the example bindings is probably the best choice. (Advanced users may also decide to keep a low-level type in ImTextureID, and use ImDrawList callback and pass information to their renderer) @@ -573,7 +604,7 @@ CODE // Cast our texture type to ImTextureID / void* MyTexture* texture = g_CoffeeTableTexture; - ImGui::Image((void*)texture, ImVec2(texture->Width, texture->Height)); + ImGui::Image((void*)texture, ImVec2(texture->Width, texture->Height)); The renderer function called after ImGui::Render() will receive that same value that the user code passed: @@ -606,7 +637,7 @@ CODE // Now that we have an OpenGL texture, assuming our imgui rendering function (imgui_impl_xxx.cpp file) takes GLuint as ImTextureID, we can display it: ImGui::Image((void*)(intptr_t)my_opengl_texture, ImVec2(my_image_width, my_image_height)); - C/C++ tip: a void* is pointer-sized storage. You may safely store any pointer or integer into it by casting your value to ImTexture / void*, and vice-versa. + C/C++ tip: a void* is pointer-sized storage. You may safely store any pointer or integer into it by casting your value to ImTexture / void*, and vice-versa. Because both end-points (user code and rendering function) are under your control, you know exactly what is stored inside the ImTexture / void*. Examples: @@ -622,13 +653,13 @@ CODE Finally, you may call ImGui::ShowMetricsWindow() to explore/visualize/understand how the ImDrawList are generated. - Q: How can I have multiple widgets with the same label or without a label? + Q: How can I have multiple widgets with the same label or with an empty label? Q: I have multiple widgets with the same label, and only the first one works. Why is that? A: A primer on labels and the ID Stack... Dear ImGui internally need to uniquely identify UI elements. Elements that are typically not clickable (such as calls to the Text functions) don't need an ID. - Interactive widgets (such as calls to Button buttons) need a unique ID. + Interactive widgets (such as calls to Button buttons) need a unique ID. Unique ID are used internally to track active widgets and occasionally associate state to widgets. Unique ID are implicitly built from the hash of multiple elements that identify the "path" to the UI element. @@ -644,6 +675,9 @@ CODE Begin("MyWindow"); Button("OK"); // Label = "OK", ID = hash of ("MyWindow", "OK") End(); + Begin("MyOtherWindow"); + Button("OK"); // Label = "OK", ID = hash of ("MyOtherWindow", "OK") + End(); - If you have a same ID twice in the same location, you'll have a conflict: @@ -672,8 +706,8 @@ CODE you to animate labels. For example you may want to include varying information in a window title bar, but windows are uniquely identified by their ID. Use "###" to pass a label that isn't part of ID: - Button("Hello###ID"; // Label = "Hello", ID = hash of (..., "ID") - Button("World###ID"; // Label = "World", ID = hash of (..., "ID") // Same as above, even though the label looks different + Button("Hello###ID"); // Label = "Hello", ID = hash of (..., "###ID") + Button("World###ID"); // Label = "World", ID = hash of (..., "###ID") // Same as above, even though the label looks different sprintf(buf, "My game (%f FPS)###MyGame", fps); Begin(buf); // Variable title, ID = hash of "MyGame" @@ -683,47 +717,48 @@ CODE within the same window. This is the most convenient way of distinguishing ID when iterating and creating many UI elements programmatically. You can push a pointer, a string or an integer value into the ID stack. - Remember that ID are formed from the concatenation of _everything_ in the ID stack! + Remember that ID are formed from the concatenation of _everything_ pushed into the ID stack. + At each level of the stack we store the seed used for items at this level of the ID stack. - Begin("Window"); + Begin("Window"); for (int i = 0; i < 100; i++) { - PushID(i); // Push i to the id tack - Button("Click"); // Label = "Click", ID = Hash of ("Window", i, "Click") + PushID(i); // Push i to the id tack + Button("Click"); // Label = "Click", ID = hash of ("Window", i, "Click") PopID(); } for (int i = 0; i < 100; i++) { MyObject* obj = Objects[i]; PushID(obj); - Button("Click"); // Label = "Click", ID = Hash of ("Window", obj pointer, "Click") + Button("Click"); // Label = "Click", ID = hash of ("Window", obj pointer, "Click") PopID(); } for (int i = 0; i < 100; i++) { MyObject* obj = Objects[i]; PushID(obj->Name); - Button("Click"); // Label = "Click", ID = Hash of ("Window", obj->Name, "Click") + Button("Click"); // Label = "Click", ID = hash of ("Window", obj->Name, "Click") PopID(); } End(); - - More example showing that you can stack multiple prefixes into the ID stack: + - You can stack multiple prefixes into the ID stack: - Button("Click"); // Label = "Click", ID = hash of (..., "Click") + Button("Click"); // Label = "Click", ID = hash of (..., "Click") PushID("node"); - Button("Click"); // Label = "Click", ID = hash of (..., "node", "Click") + Button("Click"); // Label = "Click", ID = hash of (..., "node", "Click") PushID(my_ptr); - Button("Click"); // Label = "Click", ID = hash of (..., "node", my_ptr, "Click") + Button("Click"); // Label = "Click", ID = hash of (..., "node", my_ptr, "Click") PopID(); PopID(); - Tree nodes implicitly creates a scope for you by calling PushID(). - Button("Click"); // Label = "Click", ID = hash of (..., "Click") - if (TreeNode("node")) + Button("Click"); // Label = "Click", ID = hash of (..., "Click") + if (TreeNode("node")) // <-- this function call will do a PushID() for you (unless instructed not to, with a special flag) { - Button("Click"); // Label = "Click", ID = hash of (..., "node", "Click") + Button("Click"); // Label = "Click", ID = hash of (..., "node", "Click") TreePop(); } @@ -734,16 +769,17 @@ CODE e.g. when displaying a list of objects, using indices or pointers as ID will preserve the node open/closed state differently. See what makes more sense in your situation! - Q: How can I use my own math types instead of ImVec2/ImVec4? + Q: How can I use my own math types instead of ImVec2/ImVec4? A: You can edit imconfig.h and setup the IM_VEC2_CLASS_EXTRA/IM_VEC4_CLASS_EXTRA macros to add implicit type conversions. - This way you'll be able to use your own types everywhere, e.g. passsing glm::vec2 to ImGui functions instead of ImVec2. + This way you'll be able to use your own types everywhere, e.g. passing glm::vec2 to ImGui functions instead of ImVec2. Q: How can I load a different font than the default? A: Use the font atlas to load the TTF/OTF file you want: ImGuiIO& io = ImGui::GetIO(); io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels); io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8() - Default is ProggyClean.ttf, rendered at size 13, embedded in dear imgui's source code. + Default is ProggyClean.ttf, monospace, rendered at size 13, embedded in dear imgui's source code. + (Tip: monospace fonts are convenient because they allow to facilitate horizontal alignment directly at the string level.) (Read the 'misc/fonts/README.txt' file for more details about font loading.) New programmers: remember that in C/C++ and most programming languages if you want to use a @@ -754,7 +790,8 @@ CODE Q: How can I easily use icons in my application? A: The most convenient and practical way is to merge an icon font such as FontAwesome inside you - main font. Then you can refer to icons within your strings. + main font. Then you can refer to icons within your strings. + You may want to see ImFontConfig::GlyphMinAdvanceX to make your icon look monospace to facilitate alignment. (Read the 'misc/fonts/README.txt' file for more details about icons font loading.) Q: How can I load multiple fonts? @@ -771,9 +808,9 @@ CODE // Options ImFontConfig config; - config.OversampleH = 3; + config.OversampleH = 2; config.OversampleV = 1; - config.GlyphOffset.y -= 2.0f; // Move everything by 2 pixels up + config.GlyphOffset.y -= 1.0f; // Move everything by 1 pixels up config.GlyphExtraSpacing.x = 1.0f; // Increase spacing between characters io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_pixels, &config); @@ -793,7 +830,7 @@ CODE // Or create your own custom ranges (e.g. for a game you can feed your entire game script and only build the characters the game need) ImVector ranges; - ImFontAtlas::GlyphRangesBuilder builder; + ImFontGlyphRangesBuilder builder; builder.AddText("Hello world"); // Add a string (here "Hello world" contains 7 unique characters) builder.AddChar(0x7262); // Add a specific character builder.AddRanges(io.Fonts->GetGlyphRangesJapanese()); // Add one of the default ranges @@ -805,20 +842,58 @@ CODE (such as CP-923 for Japanese or CP-1251 for Cyrillic) will NOT work! Otherwise you can convert yourself to UTF-8 or load text data from file already saved as UTF-8. - Text input: it is up to your application to pass the right character code by calling io.AddInputCharacter(). - The applications in examples/ are doing that. + Text input: it is up to your application to pass the right character code by calling io.AddInputCharacter(). + The applications in examples/ are doing that. Windows: you can use the WM_CHAR or WM_UNICHAR or WM_IME_CHAR message (depending if your app is built using Unicode or MultiByte mode). You may also use MultiByteToWideChar() or ToUnicode() to retrieve Unicode codepoints from MultiByte characters or keyboard state. - Windows: if your language is relying on an Input Method Editor (IME), you copy the HWND of your window to io.ImeWindowHandle in order for + Windows: if your language is relying on an Input Method Editor (IME), you copy the HWND of your window to io.ImeWindowHandle in order for the default implementation of io.ImeSetInputScreenPosFn() to set your Microsoft IME position correctly. + Q: How can I interact with standard C++ types (such as std::string and std::vector)? + A: - Being highly portable (bindings for several languages, frameworks, programming style, obscure or older platforms/compilers), + and aiming for compatibility & performance suitable for every modern real-time game engines, dear imgui does not use + any of std C++ types. We use raw types (e.g. char* instead of std::string) because they adapt to more use cases. + - To use ImGui::InputText() with a std::string or any resizable string class, see misc/cpp/imgui_stdlib.h. + - To use combo boxes and list boxes with std::vector or any other data structure: the BeginCombo()/EndCombo() API + lets you iterate and submit items yourself, so does the ListBoxHeader()/ListBoxFooter() API. + Prefer using them over the old and awkward Combo()/ListBox() api. + - Generally for most high-level types you should be able to access the underlying data type. + You may write your own one-liner wrappers to facilitate user code (tip: add new functions in ImGui:: namespace from your code). + - Dear ImGui applications often need to make intensive use of strings. It is expected that many of the strings you will pass + to the API are raw literals (free in C/C++) or allocated in a manner that won't incur a large cost on your application. + Please bear in mind that using std::string on applications with large amount of UI may incur unsatisfactory performances. + Modern implementations of std::string often include small-string optimization (which is often a local buffer) but those + are not configurable and not the same across implementations. + - If you are finding your UI traversal cost to be too large, make sure your string usage is not leading to excessive amount + of heap allocations. Consider using literals, statically sized buffers and your own helper functions. A common pattern + is that you will need to build lots of strings on the fly, and their maximum length can be easily be scoped ahead. + One possible implementation of a helper to facilitate printf-style building of strings: https://github.com/ocornut/Str + This is a small helper where you can instance strings with configurable local buffers length. Many game engines will + provide similar or better string helpers. + Q: How can I use the drawing facilities without an ImGui window? (using ImDrawList API) A: - You can create a dummy window. Call Begin() with the NoBackground | NoDecoration | NoSavedSettings | NoInputs flags. (The ImGuiWindowFlags_NoDecoration flag itself is a shortcut for NoTitleBar | NoResize | NoScrollbar | NoCollapse) Then you can retrieve the ImDrawList* via GetWindowDrawList() and draw to it in any way you like. - - You can call ImGui::GetOverlayDrawList() and use this draw list to display contents over every other imgui windows. - - You can create your own ImDrawList instance. You'll need to initialize them ImGui::GetDrawListSharedData(), or create your own ImDrawListSharedData, - and then call your rendered code with your own ImDrawList or ImDrawData data. + - You can call ImGui::GetBackgroundDrawList() or ImGui::GetForegroundDrawList() and use those draw list to display + contents behind or over every other imgui windows. + - You can create your own ImDrawList instance. You'll need to initialize them ImGui::GetDrawListSharedData(), or create + your own ImDrawListSharedData, and then call your rendered code with your own ImDrawList or ImDrawData data. + + Q: How can I use this without a mouse, without a keyboard or without a screen? (gamepad, input share, remote display) + A: - You can control Dear ImGui with a gamepad. Read about navigation in "Using gamepad/keyboard navigation controls". + (short version: map gamepad inputs into the io.NavInputs[] array + set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad) + - You can share your computer mouse seamlessly with your console/tablet/phone using Synergy (https://symless.com/synergy) + This is the preferred solution for developer productivity. + In particular, the "micro-synergy-client" repository (https://github.com/symless/micro-synergy-client) has simple + and portable source code (uSynergy.c/.h) for a small embeddable client that you can use on any platform to connect + to your host computer, based on the Synergy 1.x protocol. Make sure you download the Synergy 1 server on your computer. + Console SDK also sometimes provide equivalent tooling or wrapper for Synergy-like protocols. + - You may also use a third party solution such as Remote ImGui (https://github.com/JordiRos/remoteimgui) which sends + the vertices to render over the local network, allowing you to use Dear ImGui even on a screen-less machine. + - For touch inputs, you can increase the hit box of widgets (via the style.TouchPadding setting) to accommodate + for the lack of precision of touch inputs, but it is recommended you use a mouse or gamepad to allow optimizing + for screen real-estate and precision. Q: I integrated Dear ImGui in my engine and the text or lines are blurry.. A: In your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f). @@ -829,10 +904,12 @@ CODE Rectangles provided by ImGui are defined as (x1=left,y1=top,x2=right,y2=bottom) and NOT as (x1,y1,width,height). Q: How can I help? - A: - If you are experienced with Dear ImGui and C++, look at the github issues, or docs/TODO.txt and see how you want/can help! - - Convince your company to sponsor/fund development! Individual users: you can also become a Patron (patreon.com/imgui) or donate on PayPal! See README. + A: - If you are experienced with Dear ImGui and C++, look at the github issues, look at the Wiki, read docs/TODO.txt + and see how you want to help and can help! + - Businesses: convince your company to fund development via support contracts/sponsoring! This is among the most useful thing you can do for dear imgui. + - Individuals: you can also become a Patron (http://www.patreon.com/imgui) or donate on PayPal! See README. - Disclose your usage of dear imgui via a dev blog post, a tweet, a screenshot, a mention somewhere etc. - You may post screenshot or links in the gallery threads (github.com/ocornut/imgui/issues/1269). Visuals are ideal as they inspire other programmers. + You may post screenshot or links in the gallery threads (github.com/ocornut/imgui/issues/1902). Visuals are ideal as they inspire other programmers. But even without visuals, disclosing your use of dear imgui help the library grow credibility, and help other teams and programmers with taking decisions. - If you have issues or if you need to hack into the library, even if you don't expect any support it is useful that you share your issues (on github or privately). @@ -865,8 +942,8 @@ CODE #endif // Debug options -#define IMGUI_DEBUG_NAV_SCORING 0 -#define IMGUI_DEBUG_NAV_RECTS 0 +#define IMGUI_DEBUG_NAV_SCORING 0 // Display navigation scoring preview when hovering items. Display last moving direction matches when holding CTRL +#define IMGUI_DEBUG_NAV_RECTS 0 // Display the reference navigation rectangle for each window // Visual Studio warnings #ifdef _MSC_VER @@ -881,10 +958,16 @@ CODE #pragma clang diagnostic ignored "-Wfloat-equal" // warning : comparing floating point with == or != is unsafe // storing and comparing against same constants (typically 0.0f) is ok. #pragma clang diagnostic ignored "-Wformat-nonliteral" // warning : format string is not a string literal // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code. #pragma clang diagnostic ignored "-Wexit-time-destructors" // warning : declaration requires an exit-time destructor // exit-time destruction order is undefined. if MemFree() leads to users code that has been disabled before exit it might cause problems. ImGui coding style welcomes static/globals. -#pragma clang diagnostic ignored "-Wglobal-constructors" // warning : declaration requires a global destructor // similar to above, not sure what the exact difference it. +#pragma clang diagnostic ignored "-Wglobal-constructors" // warning : declaration requires a global destructor // similar to above, not sure what the exact difference is. #pragma clang diagnostic ignored "-Wsign-conversion" // warning : implicit conversion changes signedness // #pragma clang diagnostic ignored "-Wformat-pedantic" // warning : format specifies type 'void *' but the argument has type 'xxxx *' // unreasonable, would lead to casting every %p arg to void*. probably enabled by -pedantic. -#pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning : cast to 'void *' from smaller integer type 'int' +#pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning : cast to 'void *' from smaller integer type 'int' +#if __has_warning("-Wzero-as-null-pointer-constant") +#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning : zero as null pointer constant // some standard header variations use #define NULL 0 +#endif +#if __has_warning("-Wdouble-promotion") +#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double. +#endif #elif defined(__GNUC__) #pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used #pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size @@ -902,25 +985,21 @@ CODE static const float NAV_WINDOWING_HIGHLIGHT_DELAY = 0.20f; // Time before the highlight and screen dimming starts fading in static const float NAV_WINDOWING_LIST_APPEAR_DELAY = 0.15f; // Time before the window list starts to appear -// Window resizing from edges (when io.ConfigResizeWindowsFromEdges = true) -static const float RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS = 4.0f; // Extend outside and inside windows. Affect FindHoveredWindow(). -static const float RESIZE_WINDOWS_FROM_EDGES_FEEDBACK_TIMER = 0.04f; // Reduce visual noise by only highlighting the border after a certain time. +// Window resizing from edges (when io.ConfigWindowsResizeFromEdges = true and ImGuiBackendFlags_HasMouseCursors is set in io.BackendFlags by back-end) +static const float WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS = 4.0f; // Extend outside and inside windows. Affect FindHoveredWindow(). +static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f; // Reduce visual noise by only highlighting the border after a certain time. //------------------------------------------------------------------------- // [SECTION] FORWARD DECLARATIONS //------------------------------------------------------------------------- static void SetCurrentWindow(ImGuiWindow* window); -static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond); -static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond); -static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond); static void FindHoveredWindow(); static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags); static void CheckStacksSize(ImGuiWindow* window, bool write); static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges); static void AddDrawListToDrawData(ImVector* out_list, ImDrawList* draw_list); -static void AddWindowToDrawData(ImVector* out_list, ImGuiWindow* window); static void AddWindowToSortBuffer(ImVector* out_sorted_windows, ImGuiWindow* window); static ImRect GetViewportRect(); @@ -946,7 +1025,7 @@ static void NavUpdateWindowingList(); static void NavUpdateMoveResult(); static float NavUpdatePageUpPageDown(int allowed_dir_flags); static inline void NavUpdateAnyRequestFlag(); -static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id); +static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, ImGuiID id); static ImVec2 NavCalcPreferredRefPos(); static void NavSaveLastChildNavWindow(ImGuiWindow* nav_window); static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window); @@ -955,26 +1034,28 @@ static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window); static void UpdateMouseInputs(); static void UpdateMouseWheel(); static void UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4]); -} +static void RenderOuterBorders(ImGuiWindow* window); -// Test engine hooks (imgui-test) -//#define IMGUI_ENABLE_TEST_ENGINE_HOOKS -#ifdef IMGUI_ENABLE_TEST_ENGINE_HOOKS -extern void ImGuiTestEngineHook_PreNewFrame(); -extern void ImGuiTestEngineHook_PostNewFrame(); -extern void ImGuiTestEngineHook_ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg); -#endif +} //----------------------------------------------------------------------------- // [SECTION] CONTEXT AND MEMORY ALLOCATORS //----------------------------------------------------------------------------- // Current context pointer. Implicitly used by all Dear ImGui functions. Always assumed to be != NULL. -// CreateContext() will automatically set this pointer if it is NULL. Change to a different context by calling ImGui::SetCurrentContext(). -// If you use DLL hotreloading you might need to call SetCurrentContext() after reloading code from this file. -// ImGui functions are not thread-safe because of this pointer. If you want thread-safety to allow N threads to access N different contexts, you can: -// - Change this variable to use thread local storage. You may #define GImGui in imconfig.h for that purpose. Future development aim to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586 -// - Having multiple instances of the ImGui code compiled inside different namespace (easiest/safest, if you have a finite number of contexts) +// ImGui::CreateContext() will automatically set this pointer if it is NULL. Change to a different context by calling ImGui::SetCurrentContext(). +// 1) Important: globals are not shared across DLL boundaries! If you use DLLs or any form of hot-reloading: you will need to call +// SetCurrentContext() (with the pointer you got from CreateContext) from each unique static/DLL boundary, and after each hot-reloading. +// In your debugger, add GImGui to your watch window and notice how its value changes depending on which location you are currently stepping into. +// 2) Important: Dear ImGui functions are not thread-safe because of this pointer. +// If you want thread-safety to allow N threads to access N different contexts, you can: +// - Change this variable to use thread local storage so each thread can refer to a different context, in imconfig.h: +// struct ImGuiContext; +// extern thread_local ImGuiContext* MyImGuiTLS; +// #define GImGui MyImGuiTLS +// And then define MyImGuiTLS in one of your cpp file. Note that thread_local is a C++11 keyword, earlier C++ uses compiler-specific keyword. +// - Future development aim to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586 +// - If you need a finite number of contexts, you may compile and use multiple instances of the ImGui code from different namespace. #ifndef GImGui ImGuiContext* GImGui = NULL; #endif @@ -983,11 +1064,11 @@ ImGuiContext* GImGui = NULL; // If you use DLL hotreloading you might need to call SetAllocatorFunctions() after reloading code from this file. // Otherwise, you probably don't want to modify them mid-program, and if you use global/static e.g. ImVector<> instances you may need to keep them accessible during program destruction. #ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS -static void* MallocWrapper(size_t size, void* user_data) { (void)user_data; return malloc(size); } -static void FreeWrapper(void* ptr, void* user_data) { (void)user_data; free(ptr); } +static void* MallocWrapper(size_t size, void* user_data) { IM_UNUSED(user_data); return malloc(size); } +static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); free(ptr); } #else -static void* MallocWrapper(size_t size, void* user_data) { (void)user_data; (void)size; IM_ASSERT(0); return NULL; } -static void FreeWrapper(void* ptr, void* user_data) { (void)user_data; (void)ptr; IM_ASSERT(0); } +static void* MallocWrapper(size_t size, void* user_data) { IM_UNUSED(user_data); IM_UNUSED(size); IM_ASSERT(0); return NULL; } +static void FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); IM_UNUSED(ptr); IM_ASSERT(0); } #endif static void* (*GImAllocatorAllocFunc)(size_t size, void* user_data) = MallocWrapper; @@ -1022,8 +1103,11 @@ ImGuiStyle::ImGuiStyle() ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar GrabMinSize = 10.0f; // Minimum width/height of a grab box for slider/scrollbar GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs. + TabRounding = 4.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs. + TabBorderSize = 0.0f; // Thickness of border around tabs. ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text. - DisplayWindowPadding = ImVec2(20,20); // Window position are clamped to be visible within the display area by at least this amount. Only applies to regular windows. + SelectableTextAlign = ImVec2(0.0f,0.0f);// Alignment of selectable text when button is larger than text. + DisplayWindowPadding = ImVec2(19,19); // Window position are clamped to be visible within the display area by at least this amount. Only applies to regular windows. DisplaySafeAreaPadding = ImVec2(3,3); // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows. MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later. AntiAliasedLines = true; // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU. @@ -1054,6 +1138,7 @@ void ImGuiStyle::ScaleAllSizes(float scale_factor) ScrollbarRounding = ImFloor(ScrollbarRounding * scale_factor); GrabMinSize = ImFloor(GrabMinSize * scale_factor); GrabRounding = ImFloor(GrabRounding * scale_factor); + TabRounding = ImFloor(TabRounding * scale_factor); DisplayWindowPadding = ImFloor(DisplayWindowPadding * scale_factor); DisplaySafeAreaPadding = ImFloor(DisplaySafeAreaPadding * scale_factor); MouseCursorScale = ImFloor(MouseCursorScale * scale_factor); @@ -1065,8 +1150,8 @@ ImGuiIO::ImGuiIO() memset(this, 0, sizeof(*this)); // Settings - ConfigFlags = 0x00; - BackendFlags = 0x00; + ConfigFlags = ImGuiConfigFlags_None; + BackendFlags = ImGuiBackendFlags_None; DisplaySize = ImVec2(-1.0f, -1.0f); DeltaTime = 1.0f/60.0f; IniSavingRate = 5.0f; @@ -1085,18 +1170,21 @@ ImGuiIO::ImGuiIO() FontDefault = NULL; FontAllowUserScaling = false; DisplayFramebufferScale = ImVec2(1.0f, 1.0f); - DisplayVisibleMin = DisplayVisibleMax = ImVec2(0.0f, 0.0f); - // Miscellaneous configuration options + // Miscellaneous options + MouseDrawCursor = false; #ifdef __APPLE__ ConfigMacOSXBehaviors = true; // Set Mac OS X style defaults based on __APPLE__ compile time flag #else ConfigMacOSXBehaviors = false; #endif ConfigInputTextCursorBlink = true; - ConfigResizeWindowsFromEdges = false; + ConfigWindowsResizeFromEdges = true; + ConfigWindowsMoveFromTitleBarOnly = false; - // Settings (User Functions) + // Platform Functions + BackendPlatformName = BackendRendererName = NULL; + BackendPlatformUserData = BackendRendererUserData = BackendLanguageUserData = NULL; GetClipboardTextFn = GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations SetClipboardTextFn = SetClipboardTextFn_DefaultImpl; ClipboardUserData = NULL; @@ -1107,7 +1195,7 @@ ImGuiIO::ImGuiIO() RenderDrawListsFn = NULL; #endif - // Input (NB: we already have memset zero the entire structure) + // Input (NB: we already have memset zero the entire structure!) MousePos = ImVec2(-FLT_MAX, -FLT_MAX); MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX); MouseDragThreshold = 6.0f; @@ -1121,26 +1209,27 @@ ImGuiIO::ImGuiIO() // - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message void ImGuiIO::AddInputCharacter(ImWchar c) { - const int n = ImStrlenW(InputCharacters); - if (n + 1 < IM_ARRAYSIZE(InputCharacters)) + InputQueueCharacters.push_back(c); +} + +void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars) +{ + while (*utf8_chars != 0) { - InputCharacters[n] = c; - InputCharacters[n+1] = '\0'; + unsigned int c = 0; + utf8_chars += ImTextCharFromUtf8(&c, utf8_chars, NULL); + if (c > 0 && c <= 0xFFFF) + InputQueueCharacters.push_back((ImWchar)c); } } -void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars) +void ImGuiIO::ClearInputCharacters() { - // We can't pass more wchars than ImGuiIO::InputCharacters[] can hold so don't convert more - const int wchars_buf_len = sizeof(ImGuiIO::InputCharacters) / sizeof(ImWchar); - ImWchar wchars[wchars_buf_len]; - ImTextStrFromUtf8(wchars, wchars_buf_len, utf8_chars, NULL); - for (int i = 0; i < wchars_buf_len && wchars[i] != 0; i++) - AddInputCharacter(wchars[i]); + InputQueueCharacters.resize(0); } //----------------------------------------------------------------------------- -// [SECTION] MISC HELPER/UTILITIES (Maths, String, Format, Hash, File functions) +// [SECTION] MISC HELPERS/UTILITIES (Maths, String, Format, Hash, File functions) //----------------------------------------------------------------------------- ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p) @@ -1191,6 +1280,7 @@ ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, return proj_ca; } +// Consider using _stricmp/_strnicmp under Windows or strcasecmp/strncasecmp. We don't actually use either ImStricmp/ImStrnicmp in the codebase any more. int ImStricmp(const char* str1, const char* str2) { int d; @@ -1207,16 +1297,32 @@ int ImStrnicmp(const char* str1, const char* str2, size_t count) void ImStrncpy(char* dst, const char* src, size_t count) { - if (count < 1) return; - strncpy(dst, src, count); - dst[count-1] = 0; + if (count < 1) + return; + if (count > 1) + strncpy(dst, src, count - 1); + dst[count - 1] = 0; } -char* ImStrdup(const char *str) +char* ImStrdup(const char* str) { - size_t len = strlen(str) + 1; - void* buf = ImGui::MemAlloc(len); - return (char*)memcpy(buf, (const void*)str, len); + size_t len = strlen(str); + void* buf = ImGui::MemAlloc(len + 1); + return (char*)memcpy(buf, (const void*)str, len + 1); +} + +char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* src) +{ + size_t dst_buf_size = p_dst_size ? *p_dst_size : strlen(dst) + 1; + size_t src_size = strlen(src) + 1; + if (dst_buf_size < src_size) + { + ImGui::MemFree(dst); + dst = (char*)ImGui::MemAlloc(src_size); + if (p_dst_size) + *p_dst_size = src_size; + } + return (char*)memcpy(dst, (const void*)src, src_size); } const char* ImStrchrRange(const char* str, const char* str_end, char c) @@ -1290,6 +1396,12 @@ void ImStrTrimBlanks(char* buf) // B) When buf==NULL vsnprintf() will return the output size. #ifndef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS +//#define IMGUI_USE_STB_SPRINTF +#ifdef IMGUI_USE_STB_SPRINTF +#define STB_SPRINTF_IMPLEMENTATION +#include "imstb_sprintf.h" +#endif + #if defined(_MSC_VER) && !defined(vsnprintf) #define vsnprintf _vsnprintf #endif @@ -1298,7 +1410,11 @@ int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...) { va_list args; va_start(args, fmt); +#ifdef IMGUI_USE_STB_SPRINTF + int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args); +#else int w = vsnprintf(buf, buf_size, fmt, args); +#endif va_end(args); if (buf == NULL) return w; @@ -1310,7 +1426,11 @@ int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...) int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args) { +#ifdef IMGUI_USE_STB_SPRINTF + int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args); +#else int w = vsnprintf(buf, buf_size, fmt, args); +#endif if (buf == NULL) return w; if (w == -1 || w >= (int)buf_size) @@ -1320,43 +1440,69 @@ int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args) } #endif // #ifdef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS -// Pass data_size==0 for zero-terminated strings +// CRC32 needs a 1KB lookup table (not cache friendly) +// Although the code to generate the table is simple and shorter than the table itself, using a const table allows us to easily: +// - avoid an unnecessary branch/memory tap, - keep the ImHashXXX functions usable by static constructors, - make it thread-safe. +static const ImU32 GCrc32LookupTable[256] = +{ + 0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91, + 0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5, + 0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59, + 0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D, + 0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01, + 0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65, + 0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9, + 0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD, + 0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1, + 0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5, + 0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79, + 0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D, + 0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21, + 0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45, + 0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9, + 0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D, +}; + +// Known size hash +// It is ok to call ImHashData on a string with known length but the ### operator won't be supported. // FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements. -ImU32 ImHash(const void* data, int data_size, ImU32 seed) +ImU32 ImHashData(const void* data_p, size_t data_size, ImU32 seed) { - static ImU32 crc32_lut[256] = { 0 }; - if (!crc32_lut[1]) - { - const ImU32 polynomial = 0xEDB88320; - for (ImU32 i = 0; i < 256; i++) - { - ImU32 crc = i; - for (ImU32 j = 0; j < 8; j++) - crc = (crc >> 1) ^ (ImU32(-int(crc & 1)) & polynomial); - crc32_lut[i] = crc; - } - } + ImU32 crc = ~seed; + const unsigned char* data = (const unsigned char*)data_p; + const ImU32* crc32_lut = GCrc32LookupTable; + while (data_size-- != 0) + crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *data++]; + return ~crc; +} +// Zero-terminated string hash, with support for ### to reset back to seed value +// We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed. +// Because this syntax is rarely used we are optimizing for the common case. +// - If we reach ### in the string we discard the hash so far and reset to the seed. +// - We don't do 'current += 2; continue;' after handling ### to keep the code smaller/faster (measured ~10% diff in Debug build) +// FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements. +ImU32 ImHashStr(const char* data_p, size_t data_size, ImU32 seed) +{ seed = ~seed; ImU32 crc = seed; - const unsigned char* current = (const unsigned char*)data; - - if (data_size > 0) + const unsigned char* data = (const unsigned char*)data_p; + const ImU32* crc32_lut = GCrc32LookupTable; + if (data_size != 0) { - // Known size - while (data_size--) - crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *current++]; + while (data_size-- != 0) + { + unsigned char c = *data++; + if (c == '#' && data_size >= 2 && data[0] == '#' && data[1] == '#') + crc = seed; + crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c]; + } } else { - // Zero-terminated string - while (unsigned char c = *current++) + while (unsigned char c = *data++) { - // We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed. - // Because this syntax is rarely used we are optimizing for the common case. - // - If we reach ### in the string we discard the hash so far and reset to the seed. - // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller. - if (c == '#' && current[0] == '#' && current[1] == '#') + if (c == '#' && data[0] == '#' && data[1] == '#') crc = seed; crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c]; } @@ -1606,7 +1752,7 @@ int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_e } //----------------------------------------------------------------------------- -// [SECTION] MISC HELPER/UTILTIES (Color functions) +// [SECTION] MISC HELPERS/UTILTIES (Color functions) // Note: The Convert functions are early design which are not consistent with other API. //----------------------------------------------------------------------------- @@ -1719,15 +1865,15 @@ ImU32 ImGui::GetColorU32(ImU32 col) //----------------------------------------------------------------------------- // std::lower_bound but without the bullshit -static ImVector::iterator LowerBound(ImVector& data, ImGuiID key) +static ImGuiStorage::Pair* LowerBound(ImVector& data, ImGuiID key) { - ImVector::iterator first = data.begin(); - ImVector::iterator last = data.end(); + ImGuiStorage::Pair* first = data.Data; + ImGuiStorage::Pair* last = data.Data + data.Size; size_t count = (size_t)(last - first); while (count > 0) { size_t count2 = count >> 1; - ImVector::iterator mid = first + count2; + ImGuiStorage::Pair* mid = first + count2; if (mid->key < key) { first = ++mid; @@ -1760,7 +1906,7 @@ void ImGuiStorage::BuildSortByKey() int ImGuiStorage::GetInt(ImGuiID key, int default_val) const { - ImVector::iterator it = LowerBound(const_cast&>(Data), key); + ImGuiStorage::Pair* it = LowerBound(const_cast&>(Data), key); if (it == Data.end() || it->key != key) return default_val; return it->val_i; @@ -1773,7 +1919,7 @@ bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const { - ImVector::iterator it = LowerBound(const_cast&>(Data), key); + ImGuiStorage::Pair* it = LowerBound(const_cast&>(Data), key); if (it == Data.end() || it->key != key) return default_val; return it->val_f; @@ -1781,7 +1927,7 @@ float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const void* ImGuiStorage::GetVoidPtr(ImGuiID key) const { - ImVector::iterator it = LowerBound(const_cast&>(Data), key); + ImGuiStorage::Pair* it = LowerBound(const_cast&>(Data), key); if (it == Data.end() || it->key != key) return NULL; return it->val_p; @@ -1790,7 +1936,7 @@ void* ImGuiStorage::GetVoidPtr(ImGuiID key) const // References are only valid until a new value is added to the storage. Calling a Set***() function or a Get***Ref() function invalidates the pointer. int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val) { - ImVector::iterator it = LowerBound(Data, key); + ImGuiStorage::Pair* it = LowerBound(Data, key); if (it == Data.end() || it->key != key) it = Data.insert(it, Pair(key, default_val)); return &it->val_i; @@ -1803,7 +1949,7 @@ bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val) float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val) { - ImVector::iterator it = LowerBound(Data, key); + ImGuiStorage::Pair* it = LowerBound(Data, key); if (it == Data.end() || it->key != key) it = Data.insert(it, Pair(key, default_val)); return &it->val_f; @@ -1811,7 +1957,7 @@ float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val) void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val) { - ImVector::iterator it = LowerBound(Data, key); + ImGuiStorage::Pair* it = LowerBound(Data, key); if (it == Data.end() || it->key != key) it = Data.insert(it, Pair(key, default_val)); return &it->val_p; @@ -1820,7 +1966,7 @@ void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val) // FIXME-OPT: Need a way to reuse the result of lower_bound when doing GetInt()/SetInt() - not too bad because it only happens on explicit interaction (maximum one a frame) void ImGuiStorage::SetInt(ImGuiID key, int val) { - ImVector::iterator it = LowerBound(Data, key); + ImGuiStorage::Pair* it = LowerBound(Data, key); if (it == Data.end() || it->key != key) { Data.insert(it, Pair(key, val)); @@ -1836,7 +1982,7 @@ void ImGuiStorage::SetBool(ImGuiID key, bool val) void ImGuiStorage::SetFloat(ImGuiID key, float val) { - ImVector::iterator it = LowerBound(Data, key); + ImGuiStorage::Pair* it = LowerBound(Data, key); if (it == Data.end() || it->key != key) { Data.insert(it, Pair(key, val)); @@ -1847,7 +1993,7 @@ void ImGuiStorage::SetFloat(ImGuiID key, float val) void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val) { - ImVector::iterator it = LowerBound(Data, key); + ImGuiStorage::Pair* it = LowerBound(Data, key); if (it == Data.end() || it->key != key) { Data.insert(it, Pair(key, val)); @@ -1982,6 +2128,32 @@ bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const char ImGuiTextBuffer::EmptyString[1] = { 0 }; +void ImGuiTextBuffer::append(const char* str, const char* str_end) +{ + int len = str_end ? (int)(str_end - str) : (int)strlen(str); + + // Add zero-terminator the first time + const int write_off = (Buf.Size != 0) ? Buf.Size : 1; + const int needed_sz = write_off + len; + if (write_off + len >= Buf.Capacity) + { + int new_capacity = Buf.Capacity * 2; + Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity); + } + + Buf.resize(needed_sz); + memcpy(&Buf[write_off - 1], str, (size_t)len); + Buf[write_off - 1 + len] = 0; +} + +void ImGuiTextBuffer::appendf(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + appendfv(fmt, args); + va_end(args); +} + // Helper: Text buffer for logging/accumulating text void ImGuiTextBuffer::appendfv(const char* fmt, va_list args) { @@ -2000,8 +2172,8 @@ void ImGuiTextBuffer::appendfv(const char* fmt, va_list args) const int needed_sz = write_off + len; if (write_off + len >= Buf.Capacity) { - int double_capacity = Buf.Capacity * 2; - Buf.reserve(needed_sz > double_capacity ? needed_sz : double_capacity); + int new_capacity = Buf.Capacity * 2; + Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity); } Buf.resize(needed_sz); @@ -2009,14 +2181,6 @@ void ImGuiTextBuffer::appendfv(const char* fmt, va_list args) va_end(args_copy); } -void ImGuiTextBuffer::appendf(const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - appendfv(fmt, args); - va_end(args); -} - //----------------------------------------------------------------------------- // [SECTION] ImGuiListClipper // This is currently not as flexible/powerful as it should be, needs some rework (see TODO) @@ -2165,17 +2329,8 @@ void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end // Default clip_rect uses (pos_min,pos_max) // Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges) -void ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect) +void ImGui::RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_display_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect) { - // Hide anything after a '##' string - const char* text_display_end = FindRenderedTextEnd(text, text_end); - const int text_len = (int)(text_display_end - text); - if (text_len == 0) - return; - - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - // Perform CPU side clipping for single clipped element to avoid using scissor state ImVec2 pos = pos_min; const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f); @@ -2194,14 +2349,27 @@ void ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, cons if (need_clipping) { ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y); - window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect); + draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect); } else { - window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL); + draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL); } +} + +void ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect) +{ + // Hide anything after a '##' string + const char* text_display_end = FindRenderedTextEnd(text, text_end); + const int text_len = (int)(text_display_end - text); + if (text_len == 0) + return; + + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + RenderTextClippedEx(window->DrawList, pos_min, pos_max, text, text_display_end, text_size_if_known, align, clip_rect); if (g.LogEnabled) - LogRenderedText(&pos, text, text_display_end); + LogRenderedText(&pos_min, text, text_display_end); } // Render a rectangle shaped with optional rounding and borders @@ -2331,15 +2499,16 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) : DrawListInst(&context->DrawListSharedData) { Name = ImStrdup(name); - ID = ImHash(name, 0); + ID = ImHashStr(name, 0); IDStack.push_back(ID); - Flags = 0; + Flags = ImGuiWindowFlags_None; Pos = ImVec2(0.0f, 0.0f); Size = SizeFull = ImVec2(0.0f, 0.0f); SizeContents = SizeContentsExplicit = ImVec2(0.0f, 0.0f); WindowPadding = ImVec2(0.0f, 0.0f); WindowRounding = 0.0f; WindowBorderSize = 0.0f; + NameBufLen = (int)strlen(name) + 1; MoveId = GetID("#MOVE"); ChildId = 0; Scroll = ImVec2(0.0f, 0.0f); @@ -2355,6 +2524,7 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) Appearing = false; Hidden = false; HasCloseButton = false; + ResizeBorderHeld = -1; BeginCount = 0; BeginOrderWithinParent = -1; BeginOrderWithinContext = -1; @@ -2382,10 +2552,6 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) NavLastIds[0] = NavLastIds[1] = 0; NavRectRel[0] = NavRectRel[1] = ImRect(); NavLastChildNavWindow = NULL; - - FocusIdxAllCounter = FocusIdxTabCounter = -1; - FocusIdxAllRequestCurrent = FocusIdxTabRequestCurrent = INT_MAX; - FocusIdxAllRequestNext = FocusIdxTabRequestNext = INT_MAX; } ImGuiWindow::~ImGuiWindow() @@ -2399,7 +2565,7 @@ ImGuiWindow::~ImGuiWindow() ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end) { ImGuiID seed = IDStack.back(); - ImGuiID id = ImHash(str, str_end ? (int)(str_end - str) : 0, seed); + ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed); ImGui::KeepAliveID(id); return id; } @@ -2407,7 +2573,7 @@ ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end) ImGuiID ImGuiWindow::GetID(const void* ptr) { ImGuiID seed = IDStack.back(); - ImGuiID id = ImHash(&ptr, sizeof(void*), seed); + ImGuiID id = ImHashData(&ptr, sizeof(void*), seed); ImGui::KeepAliveID(id); return id; } @@ -2415,13 +2581,13 @@ ImGuiID ImGuiWindow::GetID(const void* ptr) ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end) { ImGuiID seed = IDStack.back(); - return ImHash(str, str_end ? (int)(str_end - str) : 0, seed); + return ImHashStr(str, str_end ? (str_end - str) : 0, seed); } ImGuiID ImGuiWindow::GetIDNoKeepAlive(const void* ptr) { ImGuiID seed = IDStack.back(); - return ImHash(&ptr, sizeof(void*), seed); + return ImHashData(&ptr, sizeof(void*), seed); } // This is only used in rare/specific situations to manufacture an ID out of nowhere. @@ -2429,7 +2595,7 @@ ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs) { ImGuiID seed = IDStack.back(); const int r_rel[4] = { (int)(r_abs.Min.x - Pos.x), (int)(r_abs.Min.y - Pos.y), (int)(r_abs.Max.x - Pos.x), (int)(r_abs.Max.y - Pos.y) }; - ImGuiID id = ImHash(&r_rel, sizeof(r_rel), seed); + ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed); ImGui::KeepAliveID(id); return id; } @@ -2468,6 +2634,7 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) if (g.ActiveIdIsJustActivated) { g.ActiveIdTimer = 0.0f; + g.ActiveIdHasBeenPressed = false; g.ActiveIdHasBeenEdited = false; if (id != 0) { @@ -2477,6 +2644,7 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) } g.ActiveId = id; g.ActiveIdAllowNavDirFlags = 0; + g.ActiveIdBlockNavInputFlags = 0; g.ActiveIdAllowOverlap = false; g.ActiveIdWindow = window; if (id) @@ -2486,13 +2654,14 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) } } +// FIXME-NAV: The existence of SetNavID/SetNavIDWithRectRel/SetFocusID is incredibly messy and confusing and needs some explanation or refactoring. void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window) { ImGuiContext& g = *GImGui; IM_ASSERT(id != 0); // Assume that SetFocusID() is called in the context where its NavLayer is the current layer, which is the case everywhere we call it. - const int nav_layer = window->DC.NavLayerCurrent; + const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent; if (g.NavWindow != window) g.NavInitRequest = false; g.NavId = id; @@ -2541,9 +2710,9 @@ void ImGui::MarkItemEdited(ImGuiID id) { // This marking is solely to be able to provide info for IsItemDeactivatedAfterEdit(). // ActiveId might have been released by the time we call this (as in the typical press/release button behavior) but still need need to fill the data. - (void)id; // Avoid unused variable warnings when asserts are compiled out. ImGuiContext& g = *GImGui; IM_ASSERT(g.ActiveId == id || g.ActiveId == 0 || g.DragDropActive); + IM_UNUSED(id); // Avoid unused variable warnings when asserts are compiled out. //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id); g.ActiveIdHasBeenEdited = true; g.CurrentWindow->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited; @@ -2582,7 +2751,8 @@ void ImGui::ItemSize(const ImVec2& size, float text_offset_y) const float text_base_offset = ImMax(window->DC.CurrentLineTextBaseOffset, text_offset_y); //if (g.IO.KeyAlt) window->DrawList->AddRect(window->DC.CursorPos, window->DC.CursorPos + ImVec2(size.x, line_height), IM_COL32(255,0,0,200)); // [DEBUG] window->DC.CursorPosPrevLine = ImVec2(window->DC.CursorPos.x + size.x, window->DC.CursorPos.y); - window->DC.CursorPos = ImVec2((float)(int)(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x), (float)(int)(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y)); + window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); + window->DC.CursorPos.y = (float)(int)(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y); window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x); window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y); //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG] @@ -2606,10 +2776,6 @@ void ImGui::ItemSize(const ImRect& bb, float text_offset_y) // declare their minimum size requirement to ItemSize() and then use a larger region for drawing/interaction, which is passed to ItemAdd(). bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg) { -#ifdef IMGUI_ENABLE_TEST_ENGINE_HOOKS - ImGuiTestEngineHook_ItemAdd(bb, id, nav_bb_arg); -#endif - ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; @@ -2629,7 +2795,12 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg) window->DC.LastItemId = id; window->DC.LastItemRect = bb; - window->DC.LastItemStatusFlags = 0; + window->DC.LastItemStatusFlags = ImGuiItemStatusFlags_None; + +#ifdef IMGUI_ENABLE_TEST_ENGINE + if (id != 0) + ImGuiTestEngineHook_ItemAdd(&g, nav_bb_arg ? *nav_bb_arg : bb, id); +#endif // Clipping test const bool is_clipped = IsClippedEx(bb, id, false); @@ -2679,7 +2850,8 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) if ((window->DC.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled)) return false; - // Special handling for the 1st item after Begin() which represent the title bar. When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect tht case. + // Special handling for the dummy item after Begin() which represent the title bar or tab. + // When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case. if (window->DC.LastItemId == window->MoveId && window->WriteAccessed) return false; return true; @@ -2719,26 +2891,39 @@ bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged return false; } -bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id, bool tab_stop) +// Process TAB/Shift+TAB. Be mindful that this function may _clear_ the ActiveID when tabbing out. +bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id) { ImGuiContext& g = *GImGui; + // Increment counters const bool is_tab_stop = (window->DC.ItemFlags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0; - window->FocusIdxAllCounter++; + window->DC.FocusCounterAll++; if (is_tab_stop) - window->FocusIdxTabCounter++; + window->DC.FocusCounterTab++; - // Process keyboard input at this point: TAB/Shift-TAB to tab out of the currently focused item. - // Note that we can always TAB out of a widget that doesn't allow tabbing in. - if (tab_stop && (g.ActiveId == id) && window->FocusIdxAllRequestNext == INT_MAX && window->FocusIdxTabRequestNext == INT_MAX && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab)) - window->FocusIdxTabRequestNext = window->FocusIdxTabCounter + (g.IO.KeyShift ? (is_tab_stop ? -1 : 0) : +1); // Modulo on index will be applied at the end of frame once we've got the total counter of items. + // Process TAB/Shift-TAB to tab *OUT* of the currently focused item. + // (Note that we can always TAB out of a widget that doesn't allow tabbing in) + if (g.ActiveId == id && g.FocusTabPressed && !(g.ActiveIdBlockNavInputFlags & (1 << ImGuiNavInput_KeyTab_)) && g.FocusRequestNextWindow == NULL) + { + g.FocusRequestNextWindow = window; + g.FocusRequestNextCounterTab = window->DC.FocusCounterTab + (g.IO.KeyShift ? (is_tab_stop ? -1 : 0) : +1); // Modulo on index will be applied at the end of frame once we've got the total counter of items. + } - if (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent) - return true; - if (is_tab_stop && window->FocusIdxTabCounter == window->FocusIdxTabRequestCurrent) + // Handle focus requests + if (g.FocusRequestCurrWindow == window) { - g.NavJustTabbedId = id; - return true; + if (window->DC.FocusCounterAll == g.FocusRequestCurrCounterAll) + return true; + if (is_tab_stop && window->DC.FocusCounterTab == g.FocusRequestCurrCounterTab) + { + g.NavJustTabbedId = id; + return true; + } + + // If another item is about to be focused, we clear our own active id + if (g.ActiveId == id) + ClearActiveID(); } return false; @@ -2746,8 +2931,8 @@ bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id, bool tab_stop void ImGui::FocusableItemUnregister(ImGuiWindow* window) { - window->FocusIdxAllCounter--; - window->FocusIdxTabCounter--; + window->DC.FocusCounterAll--; + window->DC.FocusCounterTab--; } ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_x, float default_y) @@ -2786,7 +2971,7 @@ void* ImGui::MemAlloc(size_t size) void ImGui::MemFree(void* ptr) { - if (ptr) + if (ptr) if (ImGuiContext* ctx = GImGui) ctx->IO.MetricsActiveAllocations--; return GImAllocatorFreeFunc(ptr, GImAllocatorUserData); @@ -2838,7 +3023,7 @@ bool ImGui::DebugCheckVersionAndDataLayout(const char* version, size_t sz_io, si return !error; } -void ImGui::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void(*free_func)(void* ptr, void* user_data), void* user_data) +void ImGui::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data) { GImAllocatorAllocFunc = alloc_func; GImAllocatorFreeFunc = free_func; @@ -2893,15 +3078,20 @@ int ImGui::GetFrameCount() return GImGui->FrameCount; } -static ImDrawList* GetOverlayDrawList(ImGuiWindow*) +ImDrawList* ImGui::GetBackgroundDrawList() { - // This seemingly unnecessary wrapper simplifies compatibility between the 'master' and 'viewport' branches. - return &GImGui->OverlayDrawList; + return &GImGui->BackgroundDrawList; } -ImDrawList* ImGui::GetOverlayDrawList() +static ImDrawList* GetForegroundDrawList(ImGuiWindow*) { - return &GImGui->OverlayDrawList; + // This seemingly unnecessary wrapper simplifies compatibility between the 'master' and 'docking' branches. + return &GImGui->ForegroundDrawList; +} + +ImDrawList* ImGui::GetForegroundDrawList() +{ + return &GImGui->ForegroundDrawList; } ImDrawListSharedData* ImGui::GetDrawListSharedData() @@ -2912,18 +3102,24 @@ ImDrawListSharedData* ImGui::GetDrawListSharedData() void ImGui::StartMouseMovingWindow(ImGuiWindow* window) { // Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows. + // We _also_ call this when clicking in a window empty space when io.ConfigWindowsMoveFromTitleBarOnly is set, but clear g.MovingWindow afterward. + // This is because we want ActiveId to be set even when the window is not permitted to move. ImGuiContext& g = *GImGui; FocusWindow(window); SetActiveID(window->MoveId, window); g.NavDisableHighlight = true; g.ActiveIdClickOffset = g.IO.MousePos - window->RootWindow->Pos; - if (!(window->Flags & ImGuiWindowFlags_NoMove) && !(window->RootWindow->Flags & ImGuiWindowFlags_NoMove)) + + bool can_move_window = true; + if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindow->Flags & ImGuiWindowFlags_NoMove)) + can_move_window = false; + if (can_move_window) g.MovingWindow = window; } // Handle mouse moving window // Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing() -void ImGui::UpdateMouseMovingWindow() +void ImGui::UpdateMouseMovingWindowNewFrame() { ImGuiContext& g = *GImGui; if (g.MovingWindow != NULL) @@ -2961,6 +3157,57 @@ void ImGui::UpdateMouseMovingWindow() } } +// Initiate moving window, handle left-click and right-click focus +void ImGui::UpdateMouseMovingWindowEndFrame() +{ + // Initiate moving window + ImGuiContext& g = *GImGui; + if (g.ActiveId != 0 || g.HoveredId != 0) + return; + + // Unless we just made a window/popup appear + if (g.NavWindow && g.NavWindow->Appearing) + return; + + // Click to focus window and start moving (after we're done with all our widgets) + if (g.IO.MouseClicked[0]) + { + if (g.HoveredRootWindow != NULL) + { + StartMouseMovingWindow(g.HoveredWindow); + if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(g.HoveredRootWindow->Flags & ImGuiWindowFlags_NoTitleBar)) + if (!g.HoveredRootWindow->TitleBarRect().Contains(g.IO.MouseClickedPos[0])) + g.MovingWindow = NULL; + } + else if (g.NavWindow != NULL && GetFrontMostPopupModal() == NULL) + { + // Clicking on void disable focus + FocusWindow(NULL); + } + } + + // With right mouse button we close popups without changing focus + // (The left mouse button path calls FocusWindow which will lead NewFrame->ClosePopupsOverWindow to trigger) + if (g.IO.MouseClicked[1]) + { + // Find the top-most window between HoveredWindow and the front most Modal Window. + // This is where we can trim the popup stack. + ImGuiWindow* modal = GetFrontMostPopupModal(); + bool hovered_window_above_modal = false; + if (modal == NULL) + hovered_window_above_modal = true; + for (int i = g.Windows.Size - 1; i >= 0 && hovered_window_above_modal == false; i--) + { + ImGuiWindow* window = g.Windows[i]; + if (window == modal) + break; + if (window == g.HoveredWindow) + hovered_window_above_modal = true; + } + ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal); + } +} + static bool IsWindowActiveAndVisible(ImGuiWindow* window) { return (window->Active) && (!window->Hidden); @@ -3129,20 +3376,21 @@ void ImGui::NewFrame() IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?"); ImGuiContext& g = *GImGui; -#ifdef IMGUI_ENABLE_TEST_ENGINE_HOOKS - ImGuiTestEngineHook_PreNewFrame(); +#ifdef IMGUI_ENABLE_TEST_ENGINE + ImGuiTestEngineHook_PreNewFrame(&g); #endif // Check user data // (We pass an error message in the assert expression to make it visible to programmers who are not using a debugger, as most assert handlers display their argument) IM_ASSERT(g.Initialized); - IM_ASSERT(g.IO.DeltaTime >= 0.0f && "Need a positive DeltaTime (zero is tolerated but will cause some timing issues)"); - IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f && "Invalid DisplaySize value"); + IM_ASSERT((g.IO.DeltaTime > 0.0f || g.FrameCount == 0) && "Need a positive DeltaTime!"); + IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount) && "Forgot to call Render() or EndFrame() at the end of the previous frame?"); + IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f && "Invalid DisplaySize value!"); IM_ASSERT(g.IO.Fonts->Fonts.Size > 0 && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?"); IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded() && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?"); - IM_ASSERT(g.Style.CurveTessellationTol > 0.0f && "Invalid style setting"); - IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f && "Invalid style setting. Alpha cannot be negative (allows us to avoid a few clamps in color computations)"); - IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount) && "Forgot to call Render() or EndFrame() at the end of the previous frame?"); + IM_ASSERT(g.Style.CurveTessellationTol > 0.0f && "Invalid style setting!"); + IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f && "Invalid style setting. Alpha cannot be negative (allows us to avoid a few clamps in color computations)!"); + IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && "Invalid style setting."); for (int n = 0; n < ImGuiKey_COUNT; n++) IM_ASSERT(g.IO.KeyMap[n] >= -1 && g.IO.KeyMap[n] < IM_ARRAYSIZE(g.IO.KeysDown) && "io.KeyMap[] contains an out of bound value (need to be 0..512, or -1 for unmapped key)"); @@ -3150,9 +3398,9 @@ void ImGui::NewFrame() if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation."); - // Perform simple check: the beta io.ConfigResizeWindowsFromEdges option requires back-end to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly. - if (g.IO.ConfigResizeWindowsFromEdges && !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseCursors)) - g.IO.ConfigResizeWindowsFromEdges = false; + // Perform simple check: the beta io.ConfigWindowsResizeFromEdges option requires back-end to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly. + if (g.IO.ConfigWindowsResizeFromEdges && !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseCursors)) + g.IO.ConfigWindowsResizeFromEdges = false; // Load settings on first frame (if not explicitly loaded manually before) if (!g.SettingsLoaded) @@ -3183,19 +3431,24 @@ void ImGui::NewFrame() g.TooltipOverrideCount = 0; g.WindowsActiveCount = 0; - // Setup current font and draw list + // Setup current font and draw list shared data g.IO.Fonts->Locked = true; SetCurrentFont(GetDefaultFont()); IM_ASSERT(g.Font->IsLoaded()); g.DrawListSharedData.ClipRectFullscreen = ImVec4(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y); g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol; - g.OverlayDrawList.Clear(); - g.OverlayDrawList.PushTextureID(g.IO.Fonts->TexID); - g.OverlayDrawList.PushClipRectFullScreen(); - g.OverlayDrawList.Flags = (g.Style.AntiAliasedLines ? ImDrawListFlags_AntiAliasedLines : 0) | (g.Style.AntiAliasedFill ? ImDrawListFlags_AntiAliasedFill : 0); + g.BackgroundDrawList.Clear(); + g.BackgroundDrawList.PushTextureID(g.IO.Fonts->TexID); + g.BackgroundDrawList.PushClipRectFullScreen(); + g.BackgroundDrawList.Flags = (g.Style.AntiAliasedLines ? ImDrawListFlags_AntiAliasedLines : 0) | (g.Style.AntiAliasedFill ? ImDrawListFlags_AntiAliasedFill : 0); + + g.ForegroundDrawList.Clear(); + g.ForegroundDrawList.PushTextureID(g.IO.Fonts->TexID); + g.ForegroundDrawList.PushClipRectFullScreen(); + g.ForegroundDrawList.Flags = (g.Style.AntiAliasedLines ? ImDrawListFlags_AntiAliasedLines : 0) | (g.Style.AntiAliasedFill ? ImDrawListFlags_AntiAliasedFill : 0); - // Mark rendering data as invalid to prevent user who may have a handle on it to use it + // Mark rendering data as invalid to prevent user who may have a handle on it to use it. g.DrawData.Clear(); // Drag and drop keep the source ID alive so even if the source disappear our state is consistent @@ -3252,7 +3505,7 @@ void ImGui::NewFrame() g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame))) : FLT_MAX; // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering) - UpdateMouseMovingWindow(); + UpdateMouseMovingWindowNewFrame(); UpdateHoveredWindowAndCaptureFlags(); // Background darkening/whitening @@ -3269,13 +3522,34 @@ void ImGui::NewFrame() UpdateMouseWheel(); // Pressing TAB activate widget focus - if (g.ActiveId == 0 && g.NavWindow != NULL && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab, false)) + g.FocusTabPressed = (g.NavWindow && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab)); + if (g.ActiveId == 0 && g.FocusTabPressed) { + // Note that SetKeyboardFocusHere() sets the Next fields mid-frame. To be consistent we also + // manipulate the Next fields even, even though they will be turned into Curr fields by the code below. + g.FocusRequestNextWindow = g.NavWindow; + g.FocusRequestNextCounterAll = INT_MAX; if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX) - g.NavWindow->FocusIdxTabRequestNext = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1); + g.FocusRequestNextCounterTab = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1); else - g.NavWindow->FocusIdxTabRequestNext = g.IO.KeyShift ? -1 : 0; + g.FocusRequestNextCounterTab = g.IO.KeyShift ? -1 : 0; } + + // Turn queued focus request into current one + g.FocusRequestCurrWindow = NULL; + g.FocusRequestCurrCounterAll = g.FocusRequestCurrCounterTab = INT_MAX; + if (g.FocusRequestNextWindow != NULL) + { + ImGuiWindow* window = g.FocusRequestNextWindow; + g.FocusRequestCurrWindow = window; + if (g.FocusRequestNextCounterAll != INT_MAX && window->DC.FocusCounterAll != -1) + g.FocusRequestCurrCounterAll = ImModPositive(g.FocusRequestNextCounterAll, window->DC.FocusCounterAll + 1); + if (g.FocusRequestNextCounterTab != INT_MAX && window->DC.FocusCounterTab != -1) + g.FocusRequestCurrCounterTab = ImModPositive(g.FocusRequestNextCounterTab, window->DC.FocusCounterTab + 1); + g.FocusRequestNextWindow = NULL; + g.FocusRequestNextCounterAll = g.FocusRequestNextCounterTab = INT_MAX; + } + g.NavIdTabCounter = INT_MAX; // Mark all windows as not visible @@ -3295,16 +3569,18 @@ void ImGui::NewFrame() // No window should be open at the beginning of the frame. // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear. g.CurrentWindowStack.resize(0); - g.CurrentPopupStack.resize(0); + g.BeginPopupStack.resize(0); ClosePopupsOverWindow(g.NavWindow); - // Create implicit window - we will only render it if the user has added something to it. + // Create implicit/fallback window - which we will only render it if the user has added something to it. // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags. + // This fallback is particularly important as it avoid ImGui:: calls from crashing. SetNextWindowSize(ImVec2(400,400), ImGuiCond_FirstUseEver); Begin("Debug##Default"); + g.FrameScopePushedImplicitWindow = true; -#ifdef IMGUI_ENABLE_TEST_ENGINE_HOOKS - ImGuiTestEngineHook_PostNewFrame(); +#ifdef IMGUI_ENABLE_TEST_ENGINE + ImGuiTestEngineHook_PostNewFrame(&g); #endif } @@ -3316,11 +3592,11 @@ void ImGui::Initialize(ImGuiContext* context) // Add .ini handle for ImGuiWindow type ImGuiSettingsHandler ini_handler; ini_handler.TypeName = "Window"; - ini_handler.TypeHash = ImHash("Window", 0, 0); + ini_handler.TypeHash = ImHashStr("Window", 0); ini_handler.ReadOpenFn = SettingsHandlerWindow_ReadOpen; ini_handler.ReadLineFn = SettingsHandlerWindow_ReadLine; ini_handler.WriteAllFn = SettingsHandlerWindow_WriteAll; - g.SettingsHandlers.push_front(ini_handler); + g.SettingsHandlers.push_back(ini_handler); g.Initialized = true; } @@ -3360,21 +3636,19 @@ void ImGui::Shutdown(ImGuiContext* context) g.CurrentWindowStack.clear(); g.WindowsById.Clear(); g.NavWindow = NULL; - g.HoveredWindow = NULL; - g.HoveredRootWindow = NULL; + g.HoveredWindow = g.HoveredRootWindow = NULL; g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL; g.MovingWindow = NULL; g.ColorModifiers.clear(); g.StyleModifiers.clear(); g.FontStack.clear(); g.OpenPopupStack.clear(); - g.CurrentPopupStack.clear(); + g.BeginPopupStack.clear(); g.DrawDataBuilder.ClearFreeMemory(); - g.OverlayDrawList.ClearFreeMemory(); + g.BackgroundDrawList.ClearFreeMemory(); + g.ForegroundDrawList.ClearFreeMemory(); g.PrivateClipboard.clear(); - g.InputTextState.TextW.clear(); - g.InputTextState.InitialText.clear(); - g.InputTextState.TempBuffer.clear(); + g.InputTextState.ClearFreeMemory(); for (int i = 0; i < g.SettingsWindows.Size; i++) IM_DELETE(g.SettingsWindows[i].Name); @@ -3386,7 +3660,7 @@ void ImGui::Shutdown(ImGuiContext* context) fclose(g.LogFile); g.LogFile = NULL; } - g.LogClipboard.clear(); + g.LogBuffer.clear(); g.Initialized = false; } @@ -3466,7 +3740,7 @@ static void AddWindowToDrawData(ImVector* out_render_list, ImGuiWin } } -static void AddWindowToDrawDataSelectLayer(ImGuiWindow* window) +static void AddRootWindowToDrawData(ImGuiWindow* window) { ImGuiContext& g = *GImGui; if (window->Flags & ImGuiWindowFlags_Tooltip) @@ -3502,6 +3776,7 @@ static void SetupDrawData(ImVector* draw_lists, ImDrawData* draw_da draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0; draw_data->DisplayPos = ImVec2(0.0f, 0.0f); draw_data->DisplaySize = io.DisplaySize; + draw_data->FramebufferScale = io.DisplayFramebufferScale; for (int n = 0; n < draw_lists->Size; n++) { draw_data->TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size; @@ -3531,25 +3806,38 @@ void ImGui::EndFrame() IM_ASSERT(g.Initialized); if (g.FrameCountEnded == g.FrameCount) // Don't process EndFrame() multiple times. return; - IM_ASSERT(g.FrameScopeActive && "Forgot to call ImGui::NewFrame()"); + IM_ASSERT(g.FrameScopeActive && "Forgot to call ImGui::NewFrame()?"); // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME) - if ( - g.FrameCountEnded == -1 || // [Ben Li & Bruno Levy], avoids an FPE because PlatformImeLastPos is not initialized - (g.IO.ImeSetInputScreenPosFn && ImLengthSqr(g.PlatformImeLastPos - g.PlatformImePos) > 0.0001f) - ) + if (g.IO.ImeSetInputScreenPosFn && (g.PlatformImeLastPos.x == FLT_MAX || ImLengthSqr(g.PlatformImeLastPos - g.PlatformImePos) > 0.0001f)) { g.IO.ImeSetInputScreenPosFn((int)g.PlatformImePos.x, (int)g.PlatformImePos.y); g.PlatformImeLastPos = g.PlatformImePos; } - // Hide implicit "Debug" window if it hasn't been used - IM_ASSERT(g.CurrentWindowStack.Size == 1); // Mismatched Begin()/End() calls, did you forget to call end on g.CurrentWindow->Name? + // Report when there is a mismatch of Begin/BeginChild vs End/EndChild calls. Important: Remember that the Begin/BeginChild API requires you + // to always call End/EndChild even if Begin/BeginChild returns false! (this is unfortunately inconsistent with most other Begin* API). + if (g.CurrentWindowStack.Size != 1) + { + if (g.CurrentWindowStack.Size > 1) + { + IM_ASSERT(g.CurrentWindowStack.Size == 1 && "Mismatched Begin/BeginChild vs End/EndChild calls: did you forget to call End/EndChild?"); + while (g.CurrentWindowStack.Size > 1) // FIXME-ERRORHANDLING + End(); + } + else + { + IM_ASSERT(g.CurrentWindowStack.Size == 1 && "Mismatched Begin/BeginChild vs End/EndChild calls: did you call End/EndChild too much?"); + } + } + + // Hide implicit/fallback "Debug" window if it hasn't been used + g.FrameScopePushedImplicitWindow = false; if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed) g.CurrentWindow->Active = false; End(); - // Show CTRL+TAB list + // Show CTRL+TAB list window if (g.NavWindowingTarget) NavUpdateWindowingList(); @@ -3570,42 +3858,12 @@ void ImGui::EndFrame() g.DragDropWithinSourceOrTarget = false; } - // Initiate moving window - if (g.ActiveId == 0 && g.HoveredId == 0) - { - if (!g.NavWindow || !g.NavWindow->Appearing) // Unless we just made a window/popup appear - { - // Click to focus window and start moving (after we're done with all our widgets) - if (g.IO.MouseClicked[0]) - { - if (g.HoveredRootWindow != NULL) - StartMouseMovingWindow(g.HoveredWindow); - else if (g.NavWindow != NULL && GetFrontMostPopupModal() == NULL) - FocusWindow(NULL); // Clicking on void disable focus - } + // End frame + g.FrameScopeActive = false; + g.FrameCountEnded = g.FrameCount; - // With right mouse button we close popups without changing focus - // (The left mouse button path calls FocusWindow which will lead NewFrame->ClosePopupsOverWindow to trigger) - if (g.IO.MouseClicked[1]) - { - // Find the top-most window between HoveredWindow and the front most Modal Window. - // This is where we can trim the popup stack. - ImGuiWindow* modal = GetFrontMostPopupModal(); - bool hovered_window_above_modal = false; - if (modal == NULL) - hovered_window_above_modal = true; - for (int i = g.Windows.Size - 1; i >= 0 && hovered_window_above_modal == false; i--) - { - ImGuiWindow* window = g.Windows[i]; - if (window == modal) - break; - if (window == g.HoveredWindow) - hovered_window_above_modal = true; - } - ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal); - } - } - } + // Initiate moving window + handle left-click and right-click focus + UpdateMouseMovingWindowEndFrame(); // Sort the window list so that all child windows are after their parent // We cannot do that on FocusWindow() because childs may not exist yet @@ -3619,7 +3877,8 @@ void ImGui::EndFrame() AddWindowToSortBuffer(&g.WindowsSortBuffer, window); } - IM_ASSERT(g.Windows.Size == g.WindowsSortBuffer.Size); // we done something wrong + // This usually assert if there is a mismatch between the ImGuiWindowFlags_ChildWindow / ParentWindow values and DC.ChildWindows[] in parents, aka we've done something wrong. + IM_ASSERT(g.Windows.Size == g.WindowsSortBuffer.Size); g.Windows.swap(g.WindowsSortBuffer); g.IO.MetricsActiveWindows = g.WindowsActiveCount; @@ -3628,11 +3887,8 @@ void ImGui::EndFrame() // Clear Input data for next frame g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f; - memset(g.IO.InputCharacters, 0, sizeof(g.IO.InputCharacters)); + g.IO.InputQueueCharacters.resize(0); memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs)); - - g.FrameScopeActive = false; - g.FrameCountEnded = g.FrameCount; } void ImGui::Render() @@ -3647,6 +3903,9 @@ void ImGui::Render() // Gather ImDrawList to render (for each active window) g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = g.IO.MetricsRenderWindows = 0; g.DrawDataBuilder.Clear(); + if (!g.BackgroundDrawList.VtxBuffer.empty()) + AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.BackgroundDrawList); + ImGuiWindow* windows_to_render_front_most[2]; windows_to_render_front_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL; windows_to_render_front_most[1] = g.NavWindowingTarget ? g.NavWindowingList : NULL; @@ -3654,26 +3913,26 @@ void ImGui::Render() { ImGuiWindow* window = g.Windows[n]; if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_front_most[0] && window != windows_to_render_front_most[1]) - AddWindowToDrawDataSelectLayer(window); + AddRootWindowToDrawData(window); } for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_front_most); n++) if (windows_to_render_front_most[n] && IsWindowActiveAndVisible(windows_to_render_front_most[n])) // NavWindowingTarget is always temporarily displayed as the front-most window - AddWindowToDrawDataSelectLayer(windows_to_render_front_most[n]); + AddRootWindowToDrawData(windows_to_render_front_most[n]); g.DrawDataBuilder.FlattenIntoSingleLayer(); // Draw software mouse cursor if requested if (g.IO.MouseDrawCursor) - RenderMouseCursor(&g.OverlayDrawList, g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor); + RenderMouseCursor(&g.ForegroundDrawList, g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor); - if (!g.OverlayDrawList.VtxBuffer.empty()) - AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.OverlayDrawList); + if (!g.ForegroundDrawList.VtxBuffer.empty()) + AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.ForegroundDrawList); // Setup ImDrawData structure for end-user SetupDrawData(&g.DrawDataBuilder.Layers[0], &g.DrawData); g.IO.MetricsRenderVertices = g.DrawData.TotalVtxCount; g.IO.MetricsRenderIndices = g.DrawData.TotalIdxCount; - // Render. If user hasn't set a callback then they may retrieve the draw data via GetDrawData() + // (Legacy) Call the Render callback function. The current prefer way is to let the user retrieve GetDrawData() and call the render function themselves. #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS if (g.DrawData.CmdListsCount > 0 && g.IO.RenderDrawListsFn != NULL) g.IO.RenderDrawListsFn(&g.DrawData); @@ -3698,11 +3957,7 @@ ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_tex return ImVec2(0.0f, font_size); ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL); - // Cancel out character spacing for the last character of a line (it is baked into glyph->AdvanceX field) - const float font_scale = font_size / font->FontSize; - const float character_spacing_x = 1.0f * font_scale; - if (text_size.x > 0.0f) - text_size.x -= character_spacing_x; + // Round text_size.x = (float)(int)(text_size.x + 0.95f); return text_size; @@ -3750,8 +4005,8 @@ void ImGui::CalcListClipping(int items_count, float items_height, int* out_items } // Find window given position, search front-to-back -// FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programatically -// with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is +// FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programatically +// with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is // called, aka before the next Begin(). Moving window isn't affected. static void FindHoveredWindow() { @@ -3762,8 +4017,8 @@ static void FindHoveredWindow() hovered_window = g.MovingWindow; ImVec2 padding_regular = g.Style.TouchExtraPadding; - ImVec2 padding_for_resize_from_edges = g.IO.ConfigResizeWindowsFromEdges ? ImMax(g.Style.TouchExtraPadding, ImVec2(RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS, RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS)) : padding_regular; - for (int i = g.Windows.Size - 1; i >= 0 && hovered_window == NULL; i--) + ImVec2 padding_for_resize_from_edges = g.IO.ConfigWindowsResizeFromEdges ? ImMax(g.Style.TouchExtraPadding, ImVec2(WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS, WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS)) : padding_regular; + for (int i = g.Windows.Size - 1; i >= 0; i--) { ImGuiWindow* window = g.Windows[i]; if (!window->Active || window->Hidden) @@ -3838,7 +4093,8 @@ int ImGui::CalcTypematicPressedRepeatAmount(float t, float t_prev, float repeat_ int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate) { ImGuiContext& g = *GImGui; - if (key_index < 0) return false; + if (key_index < 0) + return 0; IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown)); const float t = g.IO.KeysDownDuration[key_index]; return CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, repeat_delay, repeat_rate); @@ -3847,7 +4103,8 @@ int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_r bool ImGui::IsKeyPressed(int user_key_index, bool repeat) { ImGuiContext& g = *GImGui; - if (user_key_index < 0) return false; + if (user_key_index < 0) + return false; IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown)); const float t = g.IO.KeysDownDuration[user_key_index]; if (t == 0.0f) @@ -3933,20 +4190,24 @@ ImVec2 ImGui::GetMousePos() ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup() { ImGuiContext& g = *GImGui; - if (g.CurrentPopupStack.Size > 0) - return g.OpenPopupStack[g.CurrentPopupStack.Size-1].OpenMousePos; + if (g.BeginPopupStack.Size > 0) + return g.OpenPopupStack[g.BeginPopupStack.Size-1].OpenMousePos; return g.IO.MousePos; } -// We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position +// We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position. bool ImGui::IsMousePosValid(const ImVec2* mouse_pos) { - if (mouse_pos == NULL) - mouse_pos = &GImGui->IO.MousePos; + // The assert is only to silence a false-positive in XCode Static Analysis. + // Because GImGui is not dereferenced in every code path, the static analyzer assume that it may be NULL (which it doesn't for other functions). + IM_ASSERT(GImGui != NULL); const float MOUSE_INVALID = -256000.0f; - return mouse_pos->x >= MOUSE_INVALID && mouse_pos->y >= MOUSE_INVALID; + ImVec2 p = mouse_pos ? *mouse_pos : GImGui->IO.MousePos; + return p.x >= MOUSE_INVALID && p.y >= MOUSE_INVALID; } +// Return the delta from the initial clicking position. +// This is locked and return 0.0f until the mouse moves past a distance threshold at least once. // NB: This is only valid if IsMousePosValid(). Back-ends in theory should always keep mouse position valid when dragging even outside the client window. ImVec2 ImGui::GetMouseDragDelta(int button, float lock_threshold) { @@ -3999,6 +4260,18 @@ bool ImGui::IsItemActive() return false; } +bool ImGui::IsItemActivated() +{ + ImGuiContext& g = *GImGui; + if (g.ActiveId) + { + ImGuiWindow* window = g.CurrentWindow; + if (g.ActiveId == window->DC.LastItemId && g.ActiveIdPreviousFrame != window->DC.LastItemId) + return true; + } + return false; +} + bool ImGui::IsItemDeactivated() { ImGuiContext& g = *GImGui; @@ -4015,7 +4288,11 @@ bool ImGui::IsItemDeactivatedAfterEdit() bool ImGui::IsItemFocused() { ImGuiContext& g = *GImGui; - return g.NavId && !g.NavDisableHighlight && g.NavId == g.CurrentWindow->DC.LastItemId; + ImGuiWindow* window = g.CurrentWindow; + + if (g.NavId == 0 || g.NavDisableHighlight || g.NavId != window->DC.LastItemId) + return false; + return true; } bool ImGui::IsItemClicked(int mouse_button) @@ -4084,8 +4361,6 @@ ImVec2 ImGui::GetItemRectSize() static ImRect GetViewportRect() { ImGuiContext& g = *GImGui; - if (g.IO.DisplayVisibleMin.x != g.IO.DisplayVisibleMax.x && g.IO.DisplayVisibleMin.y != g.IO.DisplayVisibleMax.y) - return ImRect(g.IO.DisplayVisibleMin, g.IO.DisplayVisibleMax); return ImRect(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y); } @@ -4124,6 +4399,11 @@ static bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size child_window->ChildId = id; child_window->AutoFitChildAxises = auto_fit_axises; + // Set the cursor to handle case where the user called SetNextWindowPos()+BeginChild() manually. + // While this is not really documented/defined, it seems that the expected thing to do. + if (child_window->BeginCount == 1) + parent_window->DC.CursorPos = child_window->Pos; + // Process navigation-in immediately so NavInit can run on first frame if (g.NavActivateId == id && !(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayerActiveMask != 0 || child_window->DC.NavHasScroll)) { @@ -4211,14 +4491,14 @@ static void CheckStacksSize(ImGuiWindow* window, bool write) { // NOT checking: DC.ItemWidth, DC.AllowKeyboardFocus, DC.ButtonRepeat, DC.TextWrapPos (per window) to allow user to conveniently push once and not pop (they are cleared on Begin) ImGuiContext& g = *GImGui; - int* p_backup = &window->DC.StackSizesBackup[0]; - { int current = window->IDStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "PushID/PopID or TreeNode/TreePop Mismatch!"); p_backup++; } // Too few or too many PopID()/TreePop() - { int current = window->DC.GroupStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "BeginGroup/EndGroup Mismatch!"); p_backup++; } // Too few or too many EndGroup() - { int current = g.CurrentPopupStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "BeginMenu/EndMenu or BeginPopup/EndPopup Mismatch"); p_backup++;}// Too few or too many EndMenu()/EndPopup() + short* p_backup = &window->DC.StackSizesBackup[0]; + { int current = window->IDStack.Size; if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup == current && "PushID/PopID or TreeNode/TreePop Mismatch!"); p_backup++; } // Too few or too many PopID()/TreePop() + { int current = window->DC.GroupStack.Size; if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup == current && "BeginGroup/EndGroup Mismatch!"); p_backup++; } // Too few or too many EndGroup() + { int current = g.BeginPopupStack.Size; if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup == current && "BeginMenu/EndMenu or BeginPopup/EndPopup Mismatch"); p_backup++;}// Too few or too many EndMenu()/EndPopup() // For color, style and font stacks there is an incentive to use Push/Begin/Pop/.../End patterns, so we relax our checks a little to allow them. - { int current = g.ColorModifiers.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup >= current && "PushStyleColor/PopStyleColor Mismatch!"); p_backup++; } // Too few or too many PopStyleColor() - { int current = g.StyleModifiers.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup >= current && "PushStyleVar/PopStyleVar Mismatch!"); p_backup++; } // Too few or too many PopStyleVar() - { int current = g.FontStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup >= current && "PushFont/PopFont Mismatch!"); p_backup++; } // Too few or too many PopFont() + { int current = g.ColorModifiers.Size; if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup >= current && "PushStyleColor/PopStyleColor Mismatch!"); p_backup++; } // Too few or too many PopStyleColor() + { int current = g.StyleModifiers.Size; if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup >= current && "PushStyleVar/PopStyleVar Mismatch!"); p_backup++; } // Too few or too many PopStyleVar() + { int current = g.FontStack.Size; if (write) *p_backup = (short)current; else IM_ASSERT(*p_backup >= current && "PushFont/PopFont Mismatch!"); p_backup++; } // Too few or too many PopFont() IM_ASSERT(p_backup == window->DC.StackSizesBackup + IM_ARRAYSIZE(window->DC.StackSizesBackup)); } @@ -4237,7 +4517,7 @@ ImGuiWindow* ImGui::FindWindowByID(ImGuiID id) ImGuiWindow* ImGui::FindWindowByName(const char* name) { - ImGuiID id = ImHash(name, 0); + ImGuiID id = ImHashStr(name, 0); return FindWindowByID(id); } @@ -4258,7 +4538,7 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFl if (ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID)) { // Retrieve settings from .ini file - window->SettingsIdx = g.SettingsWindows.index_from_pointer(settings); + window->SettingsIdx = g.SettingsWindows.index_from_ptr(settings); SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false); window->Pos = ImFloor(settings->Pos); window->Collapsed = settings->Collapsed; @@ -4322,6 +4602,12 @@ static ImVec2 CalcSizeAfterConstraint(ImGuiWindow* window, ImVec2 new_size) static ImVec2 CalcSizeContents(ImGuiWindow* window) { + if (window->Collapsed) + if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) + return window->SizeContents; + if (window->Hidden && window->HiddenFramesForResize == 0 && window->HiddenFramesRegular > 0) + return window->SizeContents; + ImVec2 sz; sz.x = (float)(int)((window->SizeContentsExplicit.x != 0.0f) ? window->SizeContentsExplicit.x : (window->DC.CursorMaxPos.x - window->Pos.x + window->Scroll.x)); sz.y = (float)(int)((window->SizeContentsExplicit.y != 0.0f) ? window->SizeContentsExplicit.y : (window->DC.CursorMaxPos.y - window->Pos.y + window->Scroll.y)); @@ -4339,13 +4625,16 @@ static ImVec2 CalcSizeAutoFit(ImGuiWindow* window, const ImVec2& size_contents) } else { - // When the window cannot fit all contents (either because of constraints, either because screen is too small): we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than DisplaySize-WindowPadding. + // Maximum window size is determined by the display size const bool is_popup = (window->Flags & ImGuiWindowFlags_Popup) != 0; const bool is_menu = (window->Flags & ImGuiWindowFlags_ChildMenu) != 0; ImVec2 size_min = style.WindowMinSize; if (is_popup || is_menu) // Popups and menus bypass style.WindowMinSize by default, but we give then a non-zero minimum size to facilitate understanding problematic cases (e.g. empty popups) size_min = ImMin(size_min, ImVec2(4.0f, 4.0f)); ImVec2 size_auto_fit = ImClamp(size_contents, size_min, ImMax(size_min, g.IO.DisplaySize - style.DisplaySafeAreaPadding * 2.0f)); + + // When the window cannot fit all contents (either because of constraints, either because screen is too small), + // we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding. ImVec2 size_auto_fit_after_constraint = CalcSizeAfterConstraint(window, size_auto_fit); if (size_auto_fit_after_constraint.x < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar)) size_auto_fit.y += style.ScrollbarSize; @@ -4425,12 +4714,12 @@ static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& co struct ImGuiResizeGripDef { - ImVec2 CornerPos; + ImVec2 CornerPosN; ImVec2 InnerDir; int AngleMin12, AngleMax12; }; -const ImGuiResizeGripDef resize_grip_def[4] = +static const ImGuiResizeGripDef resize_grip_def[4] = { { ImVec2(1,1), ImVec2(-1,-1), 0, 3 }, // Lower right { ImVec2(0,1), ImVec2(+1,-1), 3, 6 }, // Lower left @@ -4442,10 +4731,10 @@ static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_ { ImRect rect = window->Rect(); if (thickness == 0.0f) rect.Max -= ImVec2(1,1); - if (border_n == 0) return ImRect(rect.Min.x + perp_padding, rect.Min.y - thickness, rect.Max.x - perp_padding, rect.Min.y + thickness); - if (border_n == 1) return ImRect(rect.Max.x - thickness, rect.Min.y + perp_padding, rect.Max.x + thickness, rect.Max.y - perp_padding); - if (border_n == 2) return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness, rect.Max.x - perp_padding, rect.Max.y + thickness); - if (border_n == 3) return ImRect(rect.Min.x - thickness, rect.Min.y + perp_padding, rect.Min.x + thickness, rect.Max.y - perp_padding); + if (border_n == 0) return ImRect(rect.Min.x + perp_padding, rect.Min.y - thickness, rect.Max.x - perp_padding, rect.Min.y + thickness); // Top + if (border_n == 1) return ImRect(rect.Max.x - thickness, rect.Min.y + perp_padding, rect.Max.x + thickness, rect.Max.y - perp_padding); // Right + if (border_n == 2) return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness, rect.Max.x - perp_padding, rect.Max.y + thickness); // Bottom + if (border_n == 3) return ImRect(rect.Min.x - thickness, rect.Min.y + perp_padding, rect.Min.x + thickness, rect.Max.y - perp_padding); // Left IM_ASSERT(0); return ImRect(); } @@ -4457,13 +4746,13 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au ImGuiWindowFlags flags = window->Flags; if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0) return; - if (window->WasActive == false) // Early out to avoid running this code for e.g. an hidden implicit Debug window. + if (window->WasActive == false) // Early out to avoid running this code for e.g. an hidden implicit/fallback Debug window. return; - const int resize_border_count = g.IO.ConfigResizeWindowsFromEdges ? 4 : 0; + const int resize_border_count = g.IO.ConfigWindowsResizeFromEdges ? 4 : 0; const float grip_draw_size = (float)(int)ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f); const float grip_hover_inner_size = (float)(int)(grip_draw_size * 0.75f); - const float grip_hover_outer_size = g.IO.ConfigResizeWindowsFromEdges ? RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS : 0.0f; + const float grip_hover_outer_size = g.IO.ConfigWindowsResizeFromEdges ? WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS : 0.0f; ImVec2 pos_target(FLT_MAX, FLT_MAX); ImVec2 size_target(FLT_MAX, FLT_MAX); @@ -4473,7 +4762,7 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++) { const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n]; - const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPos); + const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN); // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window ImRect resize_rect(corner - grip.InnerDir * grip_hover_outer_size, corner + grip.InnerDir * grip_hover_inner_size); @@ -4481,7 +4770,7 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y); bool hovered, held; ButtonBehavior(resize_rect, window->GetID((void*)(intptr_t)resize_grip_n), &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus); - //GetOverlayDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255)); + //GetForegroundDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255)); if (hovered || held) g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE; @@ -4495,8 +4784,8 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au { // Resize from any of the four corners // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position - ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + ImLerp(grip.InnerDir * grip_hover_outer_size, grip.InnerDir * -grip_hover_inner_size, grip.CornerPos); // Corner of the window corresponding to our corner grip - CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerPos, &pos_target, &size_target); + ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + ImLerp(grip.InnerDir * grip_hover_outer_size, grip.InnerDir * -grip_hover_inner_size, grip.CornerPosN); // Corner of the window corresponding to our corner grip + CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerPosN, &pos_target, &size_target); } if (resize_grip_n == 0 || held || hovered) resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip); @@ -4504,22 +4793,23 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au for (int border_n = 0; border_n < resize_border_count; border_n++) { bool hovered, held; - ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS); + ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); ButtonBehavior(border_rect, window->GetID((void*)(intptr_t)(border_n + 4)), &hovered, &held, ImGuiButtonFlags_FlattenChildren); - //GetOverlayDrawList(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255)); - if ((hovered && g.HoveredIdTimer > RESIZE_WINDOWS_FROM_EDGES_FEEDBACK_TIMER) || held) + //GetForegroundDrawLists(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255)); + if ((hovered && g.HoveredIdTimer > WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER) || held) { g.MouseCursor = (border_n & 1) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS; - if (held) *border_held = border_n; + if (held) + *border_held = border_n; } if (held) { ImVec2 border_target = window->Pos; ImVec2 border_posn; - if (border_n == 0) { border_posn = ImVec2(0, 0); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS); } - if (border_n == 1) { border_posn = ImVec2(1, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS); } - if (border_n == 2) { border_posn = ImVec2(0, 1); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS); } - if (border_n == 3) { border_posn = ImVec2(0, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS); } + if (border_n == 0) { border_posn = ImVec2(0, 0); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Top + if (border_n == 1) { border_posn = ImVec2(1, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Right + if (border_n == 2) { border_posn = ImVec2(0, 1); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Bottom + if (border_n == 3) { border_posn = ImVec2(0, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Left CalcResizePosSizeFromAnyCorner(window, border_target, border_posn, &pos_target, &size_target); } } @@ -4560,6 +4850,43 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au window->Size = window->SizeFull; } +static void ImGui::RenderOuterBorders(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + float rounding = window->WindowRounding; + float border_size = window->WindowBorderSize; + if (border_size > 0.0f && !(window->Flags & ImGuiWindowFlags_NoBackground)) + window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size); + + int border_held = window->ResizeBorderHeld; + if (border_held != -1) + { + struct ImGuiResizeBorderDef + { + ImVec2 InnerDir; + ImVec2 CornerPosN1, CornerPosN2; + float OuterAngle; + }; + static const ImGuiResizeBorderDef resize_border_def[4] = + { + { ImVec2(0,+1), ImVec2(0,0), ImVec2(1,0), IM_PI*1.50f }, // Top + { ImVec2(-1,0), ImVec2(1,0), ImVec2(1,1), IM_PI*0.00f }, // Right + { ImVec2(0,-1), ImVec2(1,1), ImVec2(0,1), IM_PI*0.50f }, // Bottom + { ImVec2(+1,0), ImVec2(0,1), ImVec2(0,0), IM_PI*1.00f } // Left + }; + const ImGuiResizeBorderDef& def = resize_border_def[border_held]; + ImRect border_r = GetResizeBorderRect(window, border_held, rounding, 0.0f); + window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.CornerPosN1) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle - IM_PI*0.25f, def.OuterAngle); + window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.CornerPosN2) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle, def.OuterAngle + IM_PI*0.25f); + window->DrawList->PathStroke(GetColorU32(ImGuiCol_SeparatorActive), false, ImMax(2.0f, border_size)); // Thicker than usual + } + if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar)) + { + float y = window->Pos.y + window->TitleBarHeight() - 1; + window->DrawList->AddLine(ImVec2(window->Pos.x + border_size, y), ImVec2(window->Pos.x + window->Size.x - border_size, y), GetColorU32(ImGuiCol_Border), g.Style.FrameBorderSize); + } +} + void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window) { window->ParentWindow = parent_window; @@ -4569,7 +4896,10 @@ void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup))) window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight; while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened) + { + IM_ASSERT(window->RootWindowForNav->ParentWindow != NULL); window->RootWindowForNav = window->RootWindowForNav->ParentWindow; + } } // Push a new ImGui window to add widgets to. @@ -4583,7 +4913,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { ImGuiContext& g = *GImGui; const ImGuiStyle& style = g.Style; - IM_ASSERT(name != NULL); // Window name required + IM_ASSERT(name != NULL && name[0] != '\0'); // Window name required IM_ASSERT(g.FrameScopeActive); // Forgot to call ImGui::NewFrame() IM_ASSERT(g.FrameCountEnded != g.FrameCount); // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet @@ -4605,6 +4935,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) const int current_frame = g.FrameCount; const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame); + + // Update Flags, LastFrameActive, BeginOrderXXX fields if (first_begin_of_the_frame) window->Flags = (ImGuiWindowFlags)flags; else @@ -4621,7 +4953,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesForResize > 0); if (flags & ImGuiWindowFlags_Popup) { - ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.CurrentPopupStack.Size]; + ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size]; window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed window_just_activated_by_user |= (window != popup_ref.Window); } @@ -4635,9 +4967,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) CheckStacksSize(window, true); if (flags & ImGuiWindowFlags_Popup) { - ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.CurrentPopupStack.Size]; + ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size]; popup_ref.Window = window; - g.CurrentPopupStack.push_back(popup_ref); + g.BeginPopupStack.push_back(popup_ref); window->PopupId = popup_ref.PopupId; } @@ -4696,12 +5028,24 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->Active = true; window->BeginOrderWithinParent = 0; - window->BeginOrderWithinContext = g.WindowsActiveCount++; + window->BeginOrderWithinContext = (short)(g.WindowsActiveCount++); window->BeginCount = 0; window->ClipRect = ImVec4(-FLT_MAX,-FLT_MAX,+FLT_MAX,+FLT_MAX); window->LastFrameActive = current_frame; window->IDStack.resize(1); + // Update stored window name when it changes (which can _only_ happen with the "###" operator, so the ID would stay unchanged). + // The title bar always display the 'name' parameter, so we only update the string storage if it needs to be visible to the end-user elsewhere. + bool window_title_visible_elsewhere = false; + if (g.NavWindowingList != NULL && (window->Flags & ImGuiWindowFlags_NoNavFocus) == 0) // Window titles visible when using CTRL+TAB + window_title_visible_elsewhere = true; + if (window_title_visible_elsewhere && !window_just_created && strcmp(name, window->Name) != 0) + { + size_t buf_len = (size_t)window->NameBufLen; + window->Name = ImStrdupcpy(window->Name, &buf_len, name); + window->NameBufLen = (int)buf_len; + } + // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS // Update contents size from last frame for auto-fitting (or use explicit size) @@ -4812,13 +5156,14 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { window->AutoPosLastDirection = ImGuiDir_None; if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api) - window->Pos = g.CurrentPopupStack.back().OpenPopupPos; + window->Pos = g.BeginPopupStack.back().OpenPopupPos; } // Position child window if (flags & ImGuiWindowFlags_ChildWindow) { - window->BeginOrderWithinParent = parent_window->DC.ChildWindows.Size; + IM_ASSERT(parent_window && parent_window->Active); + window->BeginOrderWithinParent = (short)parent_window->DC.ChildWindows.Size; parent_window->DC.ChildWindows.push_back(window); if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip) window->Pos = parent_window->DC.CursorPos; @@ -4835,12 +5180,14 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->Pos = FindBestWindowPosForPopup(window); // Clamp position so it stays visible - if (!(flags & ImGuiWindowFlags_ChildWindow)) + // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing. + if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) { - if (!window_pos_set_by_api && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && g.IO.DisplaySize.x > 0.0f && g.IO.DisplaySize.y > 0.0f) // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing. + if (g.IO.DisplaySize.x > 0.0f && g.IO.DisplaySize.y > 0.0f) // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing. { ImVec2 padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding); - window->Pos = ImMax(window->Pos + window->Size, padding) - window->Size; + ImVec2 size_for_clamping = ((g.IO.ConfigWindowsMoveFromTitleBarOnly) && !(window->Flags & ImGuiWindowFlags_NoTitleBar)) ? ImVec2(window->Size.x, window->TitleBarHeight()) : window->Size; + window->Pos = ImMax(window->Pos + size_for_clamping, padding) - size_for_clamping; window->Pos = ImMin(window->Pos, g.IO.DisplaySize - padding); } } @@ -4849,12 +5196,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies) window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding; - // Prepare for item focus requests - window->FocusIdxAllRequestCurrent = (window->FocusIdxAllRequestNext == INT_MAX || window->FocusIdxAllCounter == -1) ? INT_MAX : (window->FocusIdxAllRequestNext + (window->FocusIdxAllCounter+1)) % (window->FocusIdxAllCounter+1); - window->FocusIdxTabRequestCurrent = (window->FocusIdxTabRequestNext == INT_MAX || window->FocusIdxTabCounter == -1) ? INT_MAX : (window->FocusIdxTabRequestNext + (window->FocusIdxTabCounter+1)) % (window->FocusIdxTabCounter+1); - window->FocusIdxAllCounter = window->FocusIdxTabCounter = -1; - window->FocusIdxAllRequestNext = window->FocusIdxTabRequestNext = INT_MAX; - // Apply scrolling window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window, true); window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX); @@ -4862,16 +5203,21 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Apply window focus (new and reactivated windows are moved to front) bool want_focus = false; if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing)) - if (!(flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) || (flags & ImGuiWindowFlags_Popup)) + { + if (flags & ImGuiWindowFlags_Popup) + want_focus = true; + else if ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) == 0) want_focus = true; + } // Handle manual resize: Resize Grips, Borders, Gamepad int border_held = -1; ImU32 resize_grip_col[4] = { 0 }; - const int resize_grip_count = g.IO.ConfigResizeWindowsFromEdges ? 2 : 1; // 4 + const int resize_grip_count = g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // 4 const float grip_draw_size = (float)(int)ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f); if (!window->Collapsed) UpdateManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0]); + window->ResizeBorderHeld = (signed char)border_held; // Default item width. Make it proportional to window size if window manually resizes if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize)) @@ -4910,6 +5256,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } // Draw window + handle manual resize + // As we highlight the title bar when want_focus is set, multiple reappearing windows will have have their title bar highlighted on their reappearing frame. const float window_rounding = window->WindowRounding; const float window_border_size = window->WindowBorderSize; const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow; @@ -4930,16 +5277,21 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (!(flags & ImGuiWindowFlags_NoBackground)) { ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags)); + float alpha = 1.0f; if (g.NextWindowData.BgAlphaCond != 0) - bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(g.NextWindowData.BgAlphaVal) << IM_COL32_A_SHIFT); + alpha = g.NextWindowData.BgAlphaVal; + if (alpha != 1.0f) + bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT); window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot); } g.NextWindowData.BgAlphaCond = 0; // Title bar - ImU32 title_bar_col = GetColorU32(window->Collapsed ? ImGuiCol_TitleBgCollapsed : title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg); if (!(flags & ImGuiWindowFlags_NoTitleBar)) + { + ImU32 title_bar_col = GetColorU32(title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg); window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawCornerFlags_Top); + } // Menu bar if (flags & ImGuiWindowFlags_MenuBar) @@ -4953,9 +5305,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Scrollbars if (window->ScrollbarX) - Scrollbar(ImGuiLayoutType_Horizontal); + Scrollbar(ImGuiAxis_X); if (window->ScrollbarY) - Scrollbar(ImGuiLayoutType_Vertical); + Scrollbar(ImGuiAxis_Y); // Render resize grips (after their input handling so we don't have a frame of latency) if (!(flags & ImGuiWindowFlags_NoResize)) @@ -4963,7 +5315,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++) { const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n]; - const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPos); + const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN); window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(window_border_size, grip_draw_size) : ImVec2(grip_draw_size, window_border_size))); window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(grip_draw_size, window_border_size) : ImVec2(window_border_size, grip_draw_size))); window->DrawList->PathArcToFast(ImVec2(corner.x + grip.InnerDir.x * (window_rounding + window_border_size), corner.y + grip.InnerDir.y * (window_rounding + window_border_size)), window_rounding, grip.AngleMin12, grip.AngleMax12); @@ -4972,15 +5324,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } // Borders - if (window_border_size > 0.0f && !(flags & ImGuiWindowFlags_NoBackground)) - window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), window_rounding, ImDrawCornerFlags_All, window_border_size); - if (border_held != -1) - { - ImRect border = GetResizeBorderRect(window, border_held, grip_draw_size, 0.0f); - window->DrawList->AddLine(border.Min, border.Max, GetColorU32(ImGuiCol_SeparatorActive), ImMax(1.0f, window_border_size)); - } - if (style.FrameBorderSize > 0 && !(flags & ImGuiWindowFlags_NoTitleBar)) - window->DrawList->AddLine(title_bar_rect.GetBL() + ImVec2(style.WindowBorderSize, -1), title_bar_rect.GetBR() + ImVec2(-style.WindowBorderSize, -1), GetColorU32(ImGuiCol_Border), style.FrameBorderSize); + RenderOuterBorders(window); } // Draw navigation selection/windowing rectangle border @@ -5019,14 +5363,14 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DC.CurrentLineSize = window->DC.PrevLineSize = ImVec2(0.0f, 0.0f); window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f; window->DC.NavHideHighlightOneFrame = false; - window->DC.NavHasScroll = (GetScrollMaxY() > 0.0f); + window->DC.NavHasScroll = (GetWindowScrollMaxY(window) > 0.0f); window->DC.NavLayerActiveMask = window->DC.NavLayerActiveMaskNext; window->DC.NavLayerActiveMaskNext = 0x00; window->DC.MenuBarAppending = false; - window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; window->DC.ChildWindows.resize(0); window->DC.LayoutType = ImGuiLayoutType_Vertical; window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical; + window->DC.FocusCounterAll = window->DC.FocusCounterTab = -1; window->DC.ItemFlags = parent_window ? parent_window->DC.ItemFlags : ImGuiItemFlags_Default_; window->DC.ItemWidth = window->ItemWidthDefault; window->DC.TextWrapPos = -1.0f; // disabled @@ -5064,8 +5408,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Close & collapse button are on layer 1 (same as menus) and don't default focus const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags; window->DC.ItemFlags |= ImGuiItemFlags_NoNavDefaultFocus; - window->DC.NavLayerCurrent++; - window->DC.NavLayerCurrentMask <<= 1; + window->DC.NavLayerCurrent = ImGuiNavLayer_Menu; + window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Menu); // Collapse button if (!(flags & ImGuiWindowFlags_NoCollapse)) @@ -5081,12 +5425,15 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) *p_open = false; } - window->DC.NavLayerCurrent--; - window->DC.NavLayerCurrentMask >>= 1; + window->DC.NavLayerCurrent = ImGuiNavLayer_Main; + window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main); window->DC.ItemFlags = item_flags_backup; - // Title text (FIXME: refactor text alignment facilities along with RenderText helpers, this is too much code for what it does.) - ImVec2 text_size = CalcTextSize(name, NULL, true); + // Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker) + // FIXME: Refactor text alignment facilities along with RenderText helpers, this is too much code.. + const char* UNSAVED_DOCUMENT_MARKER = "*"; + float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? CalcTextSize(UNSAVED_DOCUMENT_MARKER, NULL, false).x : 0.0f; + ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f); ImRect text_r = title_bar_rect; float pad_left = (flags & ImGuiWindowFlags_NoCollapse) ? style.FramePadding.x : (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x); float pad_right = (p_open == NULL) ? style.FramePadding.x : (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x); @@ -5097,6 +5444,12 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) ImRect clip_rect = text_r; clip_rect.Max.x = window->Pos.x + window->Size.x - (p_open ? title_bar_rect.GetHeight() - 3 : style.FramePadding.x); // Match the size of CloseButton() RenderTextClipped(text_r.Min, text_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_rect); + if (flags & ImGuiWindowFlags_UnsavedDocument) + { + ImVec2 marker_pos = ImVec2(ImMax(text_r.Min.x, text_r.Min.x + (text_r.GetWidth() - text_size.x) * style.WindowTitleAlign.x) + text_size.x, text_r.Min.y) + ImVec2(2 - marker_size_x, 0.0f); + ImVec2 off = ImVec2(0.0f, (float)(int)(-g.FontSize * 0.25f)); + RenderTextClipped(marker_pos + off, text_r.Max + off, UNSAVED_DOCUMENT_MARKER, NULL, NULL, ImVec2(0, style.WindowTitleAlign.y), &clip_rect); + } } // Save clipped aabb so we can access it in constant-time in FindHoveredWindow() @@ -5128,7 +5481,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerMainRect.Max.x - ImMax(0.0f, ImFloor(window->WindowPadding.x*0.5f - window->WindowBorderSize))); window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerMainRect.Max.y); - // After Begin() we fill the last item / hovered data based on title bar data. It is a standard behavior (to allow creation of context menus on title bar only, etc.). + // We fill last item data based on Title Bar, in order for IsItemHovered() and IsItemActive() to be usable after Begin(). + // This is useful to allow creating context menus on title bar only, etc. window->DC.LastItemId = window->MoveId; window->DC.LastItemStatusFlags = IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0; window->DC.LastItemRect = title_bar_rect; @@ -5148,7 +5502,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Child window can be out of sight and have "negative" clip windows. // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar). IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0); - if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y) window->HiddenFramesRegular = 1; @@ -5163,7 +5516,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->HiddenFramesRegular = 1; // Update the Hidden flag - window->Hidden = (window->HiddenFramesRegular > 0) || (window->HiddenFramesForResize); + window->Hidden = (window->HiddenFramesRegular > 0) || (window->HiddenFramesForResize > 0); // Return false if we don't intend to display anything to allow user to perform an early out optimization window->SkipItems = (window->Collapsed || !window->Active || window->Hidden) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesForResize <= 0; @@ -5190,6 +5543,14 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_first_use, void ImGui::End() { ImGuiContext& g = *GImGui; + + if (g.CurrentWindowStack.Size <= 1 && g.FrameScopePushedImplicitWindow) + { + IM_ASSERT(g.CurrentWindowStack.Size > 1 && "Calling End() too many times!"); + return; // FIXME-ERRORHANDLING + } + IM_ASSERT(g.CurrentWindowStack.Size > 0); + ImGuiWindow* window = g.CurrentWindow; if (window->DC.ColumnsSet != NULL) @@ -5203,7 +5564,7 @@ void ImGui::End() // Pop from window stack g.CurrentWindowStack.pop_back(); if (window->Flags & ImGuiWindowFlags_Popup) - g.CurrentPopupStack.pop_back(); + g.BeginPopupStack.pop_back(); CheckStacksSize(window, false); SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back()); } @@ -5264,8 +5625,8 @@ void ImGui::FocusWindow(ImGuiWindow* window) g.NavInitRequest = false; g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId g.NavIdIsAlive = false; - g.NavLayer = 0; - //printf("[%05d] FocusWindow(\"%s\")\n", g.FrameCount, window ? window->Name : NULL); + g.NavLayer = ImGuiNavLayer_Main; + //IMGUI_DEBUG_LOG("FocusWindow(\"%s\")\n", window ? window->Name : NULL); } // Passing NULL allow to disable keyboard focus @@ -5292,13 +5653,15 @@ void ImGui::FocusPreviousWindowIgnoringOne(ImGuiWindow* ignore_window) ImGuiContext& g = *GImGui; for (int i = g.WindowsFocusOrder.Size - 1; i >= 0; i--) { + // We may later decide to test for different NoXXXInputs based on the active navigation input (mouse vs nav) but that may feel more confusing to the user. ImGuiWindow* window = g.WindowsFocusOrder[i]; if (window != ignore_window && window->WasActive && !(window->Flags & ImGuiWindowFlags_ChildWindow)) - { - ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(window); - FocusWindow(focus_window); - return; - } + if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) + { + ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(window); + FocusWindow(focus_window); + return; + } } } @@ -5350,7 +5713,7 @@ void ImGui::SetCurrentFont(ImFont* font) IM_ASSERT(font && font->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ? IM_ASSERT(font->Scale > 0.0f); g.Font = font; - g.FontBaseSize = g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale; + g.FontBaseSize = ImMax(1.0f, g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale); g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f; ImFontAtlas* atlas = g.Font->ContainerAtlas; @@ -5472,27 +5835,29 @@ struct ImGuiStyleVarInfo static const ImGuiStyleVarInfo GStyleVarInfo[] = { - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) }, // ImGuiStyleVar_WindowPadding - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) }, // ImGuiStyleVar_WindowRounding - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) }, // ImGuiStyleVar_WindowBorderSize - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) }, // ImGuiStyleVar_WindowMinSize - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowTitleAlign) }, // ImGuiStyleVar_WindowTitleAlign - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildRounding) }, // ImGuiStyleVar_ChildRounding - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildBorderSize) }, // ImGuiStyleVar_ChildBorderSize - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupRounding) }, // ImGuiStyleVar_PopupRounding - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupBorderSize) }, // ImGuiStyleVar_PopupBorderSize - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) }, // ImGuiStyleVar_FramePadding - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) }, // ImGuiStyleVar_FrameRounding - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameBorderSize) }, // ImGuiStyleVar_FrameBorderSize - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) }, // ImGuiStyleVar_ItemSpacing - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) }, // ImGuiStyleVar_ItemInnerSpacing - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) }, // ImGuiStyleVar_IndentSpacing - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarSize) }, // ImGuiStyleVar_ScrollbarSize - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding) }, // ImGuiStyleVar_ScrollbarRounding - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha + { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) }, // ImGuiStyleVar_WindowPadding + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) }, // ImGuiStyleVar_WindowRounding + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) }, // ImGuiStyleVar_WindowBorderSize + { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) }, // ImGuiStyleVar_WindowMinSize + { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowTitleAlign) }, // ImGuiStyleVar_WindowTitleAlign + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildRounding) }, // ImGuiStyleVar_ChildRounding + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildBorderSize) }, // ImGuiStyleVar_ChildBorderSize + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupRounding) }, // ImGuiStyleVar_PopupRounding + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupBorderSize) }, // ImGuiStyleVar_PopupBorderSize + { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) }, // ImGuiStyleVar_FramePadding + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) }, // ImGuiStyleVar_FrameRounding + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameBorderSize) }, // ImGuiStyleVar_FrameBorderSize + { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) }, // ImGuiStyleVar_ItemSpacing + { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) }, // ImGuiStyleVar_ItemInnerSpacing + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) }, // ImGuiStyleVar_IndentSpacing + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarSize) }, // ImGuiStyleVar_ScrollbarSize + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding) }, // ImGuiStyleVar_ScrollbarRounding + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, TabRounding) }, // ImGuiStyleVar_TabRounding + { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign + { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign }; static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx) @@ -5584,6 +5949,11 @@ const char* ImGui::GetStyleColorName(ImGuiCol idx) case ImGuiCol_ResizeGrip: return "ResizeGrip"; case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered"; case ImGuiCol_ResizeGripActive: return "ResizeGripActive"; + case ImGuiCol_Tab: return "Tab"; + case ImGuiCol_TabHovered: return "TabHovered"; + case ImGuiCol_TabActive: return "TabActive"; + case ImGuiCol_TabUnfocused: return "TabUnfocused"; + case ImGuiCol_TabUnfocusedActive: return "TabUnfocusedActive"; case ImGuiCol_PlotLines: return "PlotLines"; case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered"; case ImGuiCol_PlotHistogram: return "PlotHistogram"; @@ -5675,6 +6045,8 @@ bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags) } // Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext) +// Note that NoNavFocus makes the window not reachable with CTRL+TAB but it can still be focused with mouse or programmaticaly. +// If you want a window to never be focused, you may use the e.g. NoInputs flag. bool ImGui::IsWindowNavFocusable(ImGuiWindow* window) { return window->Active && window == window->RootWindow && !(window->Flags & ImGuiWindowFlags_NoNavFocus); @@ -5713,7 +6085,7 @@ void ImGui::SetWindowScrollY(ImGuiWindow* window, float new_scroll_y) window->DC.CursorMaxPos.y -= window->Scroll.y; } -static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond) +void ImGui::SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond) { // Test condition (NB: bit 0 is always true) and clear flags for next time if (cond && (window->SetWindowPosAllowFlags & cond) == 0) @@ -5748,7 +6120,7 @@ ImVec2 ImGui::GetWindowSize() return window->Size; } -static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond) +void ImGui::SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond) { // Test condition (NB: bit 0 is always true) and clear flags for next time if (cond && (window->SetWindowSizeAllowFlags & cond) == 0) @@ -5791,7 +6163,7 @@ void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond) SetWindowSize(window, size, cond); } -static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond) +void ImGui::SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond) { // Test condition (NB: bit 0 is always true) and clear flags for next time if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0) @@ -5897,7 +6269,7 @@ void ImGui::SetNextWindowBgAlpha(float alpha) g.NextWindowData.BgAlphaCond = ImGuiCond_Always; // Using a Cond member for consistency (may transition all of them to single flag set for fast Clear() op) } -// In window space (not screen space!) +// FIXME: This is in window space (not screen space!) ImVec2 ImGui::GetContentRegionMax() { ImGuiWindow* window = GetCurrentWindowRead(); @@ -6043,10 +6415,10 @@ ImVec2 ImGui::GetCursorScreenPos() return window->DC.CursorPos; } -void ImGui::SetCursorScreenPos(const ImVec2& screen_pos) +void ImGui::SetCursorScreenPos(const ImVec2& pos) { ImGuiWindow* window = GetCurrentWindow(); - window->DC.CursorPos = screen_pos; + window->DC.CursorPos = pos; window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos); } @@ -6084,12 +6456,12 @@ void ImGui::SetScrollY(float scroll_y) window->ScrollTargetCenterRatio.y = 0.0f; } -void ImGui::SetScrollFromPosY(float pos_y, float center_y_ratio) +void ImGui::SetScrollFromPosY(float local_y, float center_y_ratio) { // We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size ImGuiWindow* window = GetCurrentWindow(); IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f); - window->ScrollTarget.y = (float)(int)(pos_y + window->Scroll.y); + window->ScrollTarget.y = (float)(int)(local_y + window->Scroll.y); window->ScrollTargetCenterRatio.y = center_y_ratio; } @@ -6111,9 +6483,11 @@ void ImGui::ActivateItem(ImGuiID id) void ImGui::SetKeyboardFocusHere(int offset) { IM_ASSERT(offset >= -1); // -1 is allowed but not below - ImGuiWindow* window = GetCurrentWindow(); - window->FocusIdxAllRequestNext = window->FocusIdxAllCounter + 1 + offset; - window->FocusIdxTabRequestNext = INT_MAX; + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + g.FocusRequestNextWindow = window; + g.FocusRequestNextCounterAll = window->DC.FocusCounterAll + 1 + offset; + g.FocusRequestNextCounterTab = INT_MAX; } void ImGui::SetItemDefaultFocus() @@ -6135,71 +6509,74 @@ void ImGui::SetItemDefaultFocus() void ImGui::SetStateStorage(ImGuiStorage* tree) { - ImGuiWindow* window = GetCurrentWindow(); + ImGuiWindow* window = GImGui->CurrentWindow; window->DC.StateStorage = tree ? tree : &window->StateStorage; } ImGuiStorage* ImGui::GetStateStorage() { - ImGuiWindow* window = GetCurrentWindowRead(); + ImGuiWindow* window = GImGui->CurrentWindow; return window->DC.StateStorage; } void ImGui::PushID(const char* str_id) { - ImGuiWindow* window = GetCurrentWindowRead(); + ImGuiWindow* window = GImGui->CurrentWindow; window->IDStack.push_back(window->GetIDNoKeepAlive(str_id)); } void ImGui::PushID(const char* str_id_begin, const char* str_id_end) { - ImGuiWindow* window = GetCurrentWindowRead(); + ImGuiWindow* window = GImGui->CurrentWindow; window->IDStack.push_back(window->GetIDNoKeepAlive(str_id_begin, str_id_end)); } void ImGui::PushID(const void* ptr_id) { - ImGuiWindow* window = GetCurrentWindowRead(); + ImGuiWindow* window = GImGui->CurrentWindow; window->IDStack.push_back(window->GetIDNoKeepAlive(ptr_id)); } void ImGui::PushID(int int_id) { const void* ptr_id = (void*)(intptr_t)int_id; - ImGuiWindow* window = GetCurrentWindowRead(); + ImGuiWindow* window = GImGui->CurrentWindow; window->IDStack.push_back(window->GetIDNoKeepAlive(ptr_id)); } void ImGui::PopID() { - ImGuiWindow* window = GetCurrentWindowRead(); + ImGuiWindow* window = GImGui->CurrentWindow; window->IDStack.pop_back(); } ImGuiID ImGui::GetID(const char* str_id) { - return GImGui->CurrentWindow->GetID(str_id); + ImGuiWindow* window = GImGui->CurrentWindow; + return window->GetID(str_id); } ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end) { - return GImGui->CurrentWindow->GetID(str_id_begin, str_id_end); + ImGuiWindow* window = GImGui->CurrentWindow; + return window->GetID(str_id_begin, str_id_end); } ImGuiID ImGui::GetID(const void* ptr_id) { - return GImGui->CurrentWindow->GetID(ptr_id); + ImGuiWindow* window = GImGui->CurrentWindow; + return window->GetID(ptr_id); } bool ImGui::IsRectVisible(const ImVec2& size) { - ImGuiWindow* window = GetCurrentWindowRead(); + ImGuiWindow* window = GImGui->CurrentWindow; return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size)); } bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max) { - ImGuiWindow* window = GetCurrentWindowRead(); + ImGuiWindow* window = GImGui->CurrentWindow; return window->ClipRect.Overlaps(ImRect(rect_min, rect_max)); } @@ -6217,7 +6594,6 @@ void ImGui::BeginGroup() group_data.BackupGroupOffset = window->DC.GroupOffset; group_data.BackupCurrentLineSize = window->DC.CurrentLineSize; group_data.BackupCurrentLineTextBaseOffset = window->DC.CurrentLineTextBaseOffset; - group_data.BackupLogLinePosY = window->DC.LogLinePosY; group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive; group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive; group_data.AdvanceCursor = true; @@ -6226,14 +6602,15 @@ void ImGui::BeginGroup() window->DC.Indent = window->DC.GroupOffset; window->DC.CursorMaxPos = window->DC.CursorPos; window->DC.CurrentLineSize = ImVec2(0.0f, 0.0f); - window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; // To enforce Log carriage return + if (g.LogEnabled) + g.LogLinePosY = -FLT_MAX; // To enforce Log carriage return } void ImGui::EndGroup() { ImGuiContext& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); - IM_ASSERT(!window->DC.GroupStack.empty()); // Mismatched BeginGroup()/EndGroup() calls + IM_ASSERT(!window->DC.GroupStack.empty()); // Mismatched BeginGroup()/EndGroup() calls ImGuiGroupData& group_data = window->DC.GroupStack.back(); @@ -6246,12 +6623,13 @@ void ImGui::EndGroup() window->DC.GroupOffset = group_data.BackupGroupOffset; window->DC.CurrentLineSize = group_data.BackupCurrentLineSize; window->DC.CurrentLineTextBaseOffset = group_data.BackupCurrentLineTextBaseOffset; - window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; // To enforce Log carriage return + if (g.LogEnabled) + g.LogLinePosY = -FLT_MAX; // To enforce Log carriage return if (group_data.AdvanceCursor) { window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.PrevLineTextBaseOffset, group_data.BackupCurrentLineTextBaseOffset); // FIXME: Incorrect, we should grab the base offset from the *first line* of the group but it is hard to obtain now. - ItemSize(group_bb.GetSize(), group_data.BackupCurrentLineTextBaseOffset); + ItemSize(group_bb.GetSize(), 0.0f); ItemAdd(group_bb, 0); } @@ -6389,13 +6767,13 @@ void ImGui::SetTooltip(const char* fmt, ...) bool ImGui::IsPopupOpen(ImGuiID id) { ImGuiContext& g = *GImGui; - return g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].PopupId == id; + return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == id; } bool ImGui::IsPopupOpen(const char* str_id) { ImGuiContext& g = *GImGui; - return g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].PopupId == g.CurrentWindow->GetID(str_id); + return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == g.CurrentWindow->GetID(str_id); } ImGuiWindow* ImGui::GetFrontMostPopupModal() @@ -6422,7 +6800,7 @@ void ImGui::OpenPopupEx(ImGuiID id) { ImGuiContext& g = *GImGui; ImGuiWindow* parent_window = g.CurrentWindow; - int current_stack_size = g.CurrentPopupStack.Size; + int current_stack_size = g.BeginPopupStack.Size; ImGuiPopupRef popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack. popup_ref.PopupId = id; popup_ref.Window = NULL; @@ -6432,7 +6810,7 @@ void ImGui::OpenPopupEx(ImGuiID id) popup_ref.OpenPopupPos = NavCalcPreferredRefPos(); popup_ref.OpenMousePos = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos; - //printf("[%05d] OpenPopupEx(0x%08X)\n", g.FrameCount, id); + //IMGUI_DEBUG_LOG("OpenPopupEx(0x%08X)\n", g.FrameCount, id); if (g.OpenPopupStack.Size < current_stack_size + 1) { g.OpenPopupStack.push_back(popup_ref); @@ -6481,12 +6859,13 @@ void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window) // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it. // Don't close our own child popup windows. - int n = 0; + int popup_count_to_keep = 0; if (ref_window) { - for (n = 0; n < g.OpenPopupStack.Size; n++) + // Find the highest popup which is a descendant of the reference window (generally reference window = NavWindow) + for (; popup_count_to_keep < g.OpenPopupStack.Size; popup_count_to_keep++) { - ImGuiPopupRef& popup = g.OpenPopupStack[n]; + ImGuiPopupRef& popup = g.OpenPopupStack[popup_count_to_keep]; if (!popup.Window) continue; IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0); @@ -6494,47 +6873,73 @@ void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window) continue; // Trim the stack if popups are not direct descendant of the reference window (which is often the NavWindow) - bool has_focus = false; - for (int m = n; m < g.OpenPopupStack.Size && !has_focus; m++) - has_focus = (g.OpenPopupStack[m].Window && g.OpenPopupStack[m].Window->RootWindow == ref_window->RootWindow); - if (!has_focus) + bool popup_or_descendent_has_focus = false; + for (int m = popup_count_to_keep; m < g.OpenPopupStack.Size && !popup_or_descendent_has_focus; m++) + if (g.OpenPopupStack[m].Window && g.OpenPopupStack[m].Window->RootWindow == ref_window->RootWindow) + popup_or_descendent_has_focus = true; + if (!popup_or_descendent_has_focus) break; } } - if (n < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the block below - ClosePopupToLevel(n); + if (popup_count_to_keep < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the statement below + { + //IMGUI_DEBUG_LOG("ClosePopupsOverWindow(%s) -> ClosePopupToLevel(%d)\n", ref_window->Name, popup_count_to_keep); + ClosePopupToLevel(popup_count_to_keep, false); + } } -void ImGui::ClosePopupToLevel(int remaining) +void ImGui::ClosePopupToLevel(int remaining, bool apply_focus_to_window_under) { IM_ASSERT(remaining >= 0); ImGuiContext& g = *GImGui; ImGuiWindow* focus_window = (remaining > 0) ? g.OpenPopupStack[remaining-1].Window : g.OpenPopupStack[0].ParentWindow; - if (g.NavLayer == 0) - focus_window = NavRestoreLastChildNavWindow(focus_window); - FocusWindow(focus_window); - focus_window->DC.NavHideHighlightOneFrame = true; g.OpenPopupStack.resize(remaining); -} -void ImGui::ClosePopup(ImGuiID id) -{ - if (!IsPopupOpen(id)) - return; - ImGuiContext& g = *GImGui; - ClosePopupToLevel(g.OpenPopupStack.Size - 1); + // FIXME: This code is faulty and we may want to eventually to replace or remove the 'apply_focus_to_window_under=true' path completely. + // Instead of using g.OpenPopupStack[remaining-1].Window etc. we should find the highest root window that is behind the popups we are closing. + // The current code will set focus to the parent of the popup window which is incorrect. + // It rarely manifested until now because UpdateMouseMovingWindowNewFrame() would call FocusWindow() again on the clicked window, + // leading to a chain of focusing A (clicked window) then B (parent window of the popup) then A again. + // However if the clicked window has the _NoMove flag set we would be left with B focused. + // For now, we have disabled this path when called from ClosePopupsOverWindow() because the users of ClosePopupsOverWindow() don't need to alter focus anyway, + // but we should inspect and fix this properly. + if (apply_focus_to_window_under) + { + if (g.NavLayer == 0) + focus_window = NavRestoreLastChildNavWindow(focus_window); + FocusWindow(focus_window); + } } // Close the popup we have begin-ed into. void ImGui::CloseCurrentPopup() { ImGuiContext& g = *GImGui; - int popup_idx = g.CurrentPopupStack.Size - 1; - if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.CurrentPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId) + int popup_idx = g.BeginPopupStack.Size - 1; + if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.BeginPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId) return; - while (popup_idx > 0 && g.OpenPopupStack[popup_idx].Window && (g.OpenPopupStack[popup_idx].Window->Flags & ImGuiWindowFlags_ChildMenu)) + + // Closing a menu closes its top-most parent popup (unless a modal) + while (popup_idx > 0) + { + ImGuiWindow* popup_window = g.OpenPopupStack[popup_idx].Window; + ImGuiWindow* parent_popup_window = g.OpenPopupStack[popup_idx - 1].Window; + bool close_parent = false; + if (popup_window && (popup_window->Flags & ImGuiWindowFlags_ChildMenu)) + if (parent_popup_window == NULL || !(parent_popup_window->Flags & ImGuiWindowFlags_Modal)) + close_parent = true; + if (!close_parent) + break; popup_idx--; - ClosePopupToLevel(popup_idx); + } + //IMGUI_DEBUG_LOG("CloseCurrentPopup %d -> %d\n", g.BeginPopupStack.Size - 1, popup_idx); + ClosePopupToLevel(popup_idx, true); + + // A common pattern is to close a popup when selecting a menu item/selectable that will open another window. + // To improve this usage pattern, we avoid nav highlight for a single frame in the parent window. + // Similarly, we could avoid mouse hover highlight in this window but it is less visually problematic. + if (ImGuiWindow* window = g.NavWindow) + window->DC.NavHideHighlightOneFrame = true; } bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags) @@ -6548,7 +6953,7 @@ bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags) char name[20]; if (extra_flags & ImGuiWindowFlags_ChildMenu) - ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.CurrentPopupStack.Size); // Recycle windows based on depth + ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth else ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame @@ -6562,14 +6967,17 @@ bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags) bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags) { ImGuiContext& g = *GImGui; - if (g.OpenPopupStack.Size <= g.CurrentPopupStack.Size) // Early out for performance + if (g.OpenPopupStack.Size <= g.BeginPopupStack.Size) // Early out for performance { g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values return false; } - return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings); + flags |= ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings; + return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags); } +// If 'p_open' is specified for a modal popup window, the popup will have a regular close button which will close the popup. +// Note that popup visibility status is owned by imgui (and manipulated with e.g. OpenPopup) so the actual value of *p_open is meaningless here. bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags) { ImGuiContext& g = *GImGui; @@ -6586,12 +6994,13 @@ bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags fla if (g.NextWindowData.PosCond == 0) SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); - bool is_open = Begin(name, p_open, flags | ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings); + flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings; + const bool is_open = Begin(name, p_open, flags); if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display) { EndPopup(); if (is_open) - ClosePopup(id); + ClosePopupToLevel(g.BeginPopupStack.Size, true); return false; } return is_open; @@ -6599,9 +7008,9 @@ bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags fla void ImGui::EndPopup() { - ImGuiContext& g = *GImGui; (void)g; + ImGuiContext& g = *GImGui; IM_ASSERT(g.CurrentWindow->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginPopup()/EndPopup() calls - IM_ASSERT(g.CurrentPopupStack.Size > 0); + IM_ASSERT(g.BeginPopupStack.Size > 0); // Make all menus and popups wrap around for now, may need to expose that policy. NavMoveRequestTryWrapping(g.CurrentWindow, ImGuiNavMoveFlags_LoopY); @@ -6656,8 +7065,8 @@ ImRect ImGui::GetWindowAllowedExtentRect(ImGuiWindow*) ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy) { ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size); - //GImGui->OverlayDrawList.AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255)); - //GImGui->OverlayDrawList.AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255)); + //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255)); + //GetForegroundDrawList()->AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255)); // Combo Box policy (we want a connecting edge) if (policy == ImGuiPopupPositionPolicy_ComboBox) @@ -6713,11 +7122,11 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) ImRect r_outer = GetWindowAllowedExtentRect(window); if (window->Flags & ImGuiWindowFlags_ChildMenu) { - // Child menus typically request _any_ position within the parent menu item, and then our FindBestWindowPosForPopup() function will move the new menu outside the parent bounds. + // Child menus typically request _any_ position within the parent menu item, and then we move the new menu outside the parent bounds. // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu. IM_ASSERT(g.CurrentWindow == window); ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2]; - float horizontal_overlap = g.Style.ItemSpacing.x; // We want some overlap to convey the relative depth of each menu (currently the amount of overlap is hard-coded to style.ItemSpacing.x). + float horizontal_overlap = g.Style.ItemInnerSpacing.x; // We want some overlap to convey the relative depth of each menu (currently the amount of overlap is hard-coded to style.ItemSpacing.x). ImRect r_avoid; if (parent_window->DC.MenuBarAppending) r_avoid = ImRect(-FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight(), FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight() + parent_window->MenuBarHeight()); @@ -6749,6 +7158,7 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) return window->Pos; } + //----------------------------------------------------------------------------- // [SECTION] KEYBOARD/GAMEPAD NAVIGATION //----------------------------------------------------------------------------- @@ -6850,7 +7260,7 @@ static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) if (ImGui::IsMouseHoveringRect(cand.Min, cand.Max)) { ImFormatString(buf, IM_ARRAYSIZE(buf), "dbox (%.2f,%.2f->%.4f)\ndcen (%.2f,%.2f->%.4f)\nd (%.2f,%.2f->%.4f)\nnav %c, quadrant %c", dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, "WENS"[g.NavMoveDir], "WENS"[quadrant]); - ImDrawList* draw_list = GetOverlayDrawList(window); + ImDrawList* draw_list = ImGui::GetForegroundDrawList(window); draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100)); draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200)); draw_list->AddRectFilled(cand.Max-ImVec2(4,4), cand.Max+ImGui::CalcTextSize(buf)+ImVec2(4,4), IM_COL32(40,0,0,150)); @@ -6862,7 +7272,7 @@ static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) if (quadrant == g.NavMoveDir) { ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center); - ImDrawList* draw_list = GetOverlayDrawList(window); + ImDrawList* draw_list = ImGui::GetForegroundDrawList(window); draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200)); draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf); } @@ -6943,7 +7353,7 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con // Process Move Request (scoring for navigation) // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRectScreen + scoring from a rect wrapped according to current wrapping policy) - if ((g.NavId != id || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & ImGuiItemFlags_NoNav)) + if ((g.NavId != id || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled|ImGuiItemFlags_NoNav))) { ImGuiNavMoveResult* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; #if IMGUI_DEBUG_NAV_SCORING @@ -6957,6 +7367,7 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con if (new_best) { result->ID = id; + result->SelectScopeId = g.MultiSelectScopeId; result->Window = window; result->RectRel = nav_bb_rel; } @@ -6968,6 +7379,7 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con { result = &g.NavMoveResultLocalVisibleSet; result->ID = id; + result->SelectScopeId = g.MultiSelectScopeId; result->Window = window; result->RectRel = nav_bb_rel; } @@ -6979,7 +7391,7 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con g.NavWindow = window; // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window. g.NavLayer = window->DC.NavLayerCurrent; g.NavIdIsAlive = true; - g.NavIdTabCounter = window->FocusIdxTabCounter; + g.NavIdTabCounter = window->DC.FocusCounterTab; window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel; // Store item bounding box (relative to window position) } } @@ -7059,7 +7471,7 @@ static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window) return window->NavLastChildNavWindow ? window->NavLastChildNavWindow : window; } -static void NavRestoreLayer(int layer) +static void NavRestoreLayer(ImGuiNavLayer layer) { ImGuiContext& g = *GImGui; g.NavLayer = layer; @@ -7166,7 +7578,7 @@ ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInput static void NavScrollToBringItemIntoView(ImGuiWindow* window, const ImRect& item_rect) { ImRect window_rect(window->InnerMainRect.Min - ImVec2(1, 1), window->InnerMainRect.Max + ImVec2(1, 1)); - //g.OverlayDrawList.AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG] + //GetForegroundDrawList(window)->AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG] if (window_rect.Contains(item_rect)) return; @@ -7198,7 +7610,7 @@ static void ImGui::NavUpdate() ImGuiContext& g = *GImGui; g.IO.WantSetMousePos = false; #if 0 - if (g.NavScoringCount > 0) printf("[%05d] NavScoringCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.FrameCount, g.NavScoringCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest); + if (g.NavScoringCount > 0) IMGUI_DEBUG_LOG("NavScoringCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.FrameCount, g.NavScoringCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest); #endif // Set input source as Gamepad when buttons are pressed before we map Keyboard (some features differs when used with Gamepad vs Keyboard) @@ -7219,9 +7631,13 @@ static void ImGui::NavUpdate() NAV_MAP_KEY(ImGuiKey_RightArrow,ImGuiNavInput_KeyRight_); NAV_MAP_KEY(ImGuiKey_UpArrow, ImGuiNavInput_KeyUp_ ); NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_ ); - if (g.IO.KeyCtrl) g.IO.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f; - if (g.IO.KeyShift) g.IO.NavInputs[ImGuiNavInput_TweakFast] = 1.0f; - if (g.IO.KeyAlt) g.IO.NavInputs[ImGuiNavInput_KeyMenu_] = 1.0f; + NAV_MAP_KEY(ImGuiKey_Tab, ImGuiNavInput_KeyTab_ ); + if (g.IO.KeyCtrl) + g.IO.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f; + if (g.IO.KeyShift) + g.IO.NavInputs[ImGuiNavInput_TweakFast] = 1.0f; + if (g.IO.KeyAlt && !g.IO.KeyCtrl) // AltGR is Alt+Ctrl, also even on keyboards without AltGR we don't want Alt+Ctrl to open menu. + g.IO.NavInputs[ImGuiNavInput_KeyMenu_] = 1.0f; #undef NAV_MAP_KEY } memcpy(g.IO.NavInputsDownDurationPrev, g.IO.NavInputsDownDuration, sizeof(g.IO.NavInputsDownDuration)); @@ -7286,14 +7702,15 @@ static void ImGui::NavUpdate() // Set output flags for user application g.IO.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs); - g.IO.NavVisible = (g.IO.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL) || g.NavInitRequest; + g.IO.NavVisible = (g.IO.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL); // Process NavCancel input (to close a popup, get back to parent, clear focus) if (IsNavInputPressed(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed)) { if (g.ActiveId != 0) { - ClearActiveID(); + if (!(g.ActiveIdBlockNavInputFlags & (1 << ImGuiNavInput_Cancel))) + ClearActiveID(); } else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow) { @@ -7311,12 +7728,12 @@ static void ImGui::NavUpdate() { // Close open popup/menu if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal)) - ClosePopupToLevel(g.OpenPopupStack.Size - 1); + ClosePopupToLevel(g.OpenPopupStack.Size - 1, true); } else if (g.NavLayer != 0) { // Leave the "menu" layer - NavRestoreLayer(0); + NavRestoreLayer(ImGuiNavLayer_Main); } else { @@ -7358,7 +7775,7 @@ static void ImGui::NavUpdate() if (g.NavMoveRequestForward == ImGuiNavForward_None) { g.NavMoveDir = ImGuiDir_None; - g.NavMoveRequestFlags = 0; + g.NavMoveRequestFlags = ImGuiNavMoveFlags_None; if (g.NavWindow && !g.NavWindowingTarget && allowed_dir_flags && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) { if ((allowed_dir_flags & (1<AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG] g.NavScoringCount = 0; #if IMGUI_DEBUG_NAV_RECTS - if (g.NavWindow) { for (int layer = 0; layer < 2; layer++) GetOverlayDrawList(g.NavWindow)->AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG] - if (g.NavWindow) { ImU32 col = (!g.NavWindow->Hidden) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredRefPos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); GetOverlayDrawList(g.NavWindow)->AddCircleFilled(p, 3.0f, col); GetOverlayDrawList(g.NavWindow)->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); } + if (g.NavWindow) + { + ImDrawList* draw_list = GetForegroundDrawList(g.NavWindow); + if (1) { for (int layer = 0; layer < 2; layer++) draw_list->AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG] + if (1) { ImU32 col = (!g.NavWindow->Hidden) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredRefPos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); draw_list->AddCircleFilled(p, 3.0f, col); draw_list->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); } + } #endif } @@ -7507,8 +7928,13 @@ static void ImGui::NavUpdateMoveResult() ClearActiveID(); g.NavWindow = result->Window; + if (g.NavId != result->ID) + { + // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId) + g.NavJustMovedToId = result->ID; + g.NavJustMovedToSelectScopeId = result->SelectScopeId; + } SetNavIDWithRectRel(result->ID, g.NavLayer, result->RectRel); - g.NavJustMovedToId = result->ID; g.NavMoveFromClampedRefRect = false; } @@ -7520,7 +7946,7 @@ static float ImGui::NavUpdatePageUpPageDown(int allowed_dir_flags) ImGuiWindow* window = g.NavWindow; bool page_up_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageUp]) && (allowed_dir_flags & (1 << ImGuiDir_Up)); bool page_down_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageDown]) && (allowed_dir_flags & (1 << ImGuiDir_Down)); - if ((page_up_held && !page_down_held) || (page_down_held && !page_up_held)) + if (page_up_held != page_down_held) // If either (not both) are pressed { if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll) { @@ -7590,7 +8016,9 @@ static void NavUpdateWindowingHighlightWindow(int focus_change_dir) g.NavWindowingToggleLayer = false; } -// Window management mode (hold to: change focus/move/resize, tap to: toggle menu layer) +// Windowing management mode +// Keyboard: CTRL+Tab (change focus/move/resize), Alt (toggle menu layer) +// Gamepad: Hold Menu/Square (change focus/move/resize), Tap Menu/Square (toggle menu layer) static void ImGui::NavUpdateWindowing() { ImGuiContext& g = *GImGui; @@ -7693,13 +8121,14 @@ static void ImGui::NavUpdateWindowing() g.NavDisableMouseHover = true; apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window); ClosePopupsOverWindow(apply_focus_window); + ClearActiveID(); FocusWindow(apply_focus_window); if (apply_focus_window->NavLastIds[0] == 0) NavInitWindow(apply_focus_window, false); // If the window only has a menu layer, select it directly - if (apply_focus_window->DC.NavLayerActiveMask == (1 << 1)) - g.NavLayer = 1; + if (apply_focus_window->DC.NavLayerActiveMask == (1 << ImGuiNavLayer_Menu)) + g.NavLayer = ImGuiNavLayer_Menu; } if (apply_focus_window) g.NavWindowingTarget = NULL; @@ -7709,7 +8138,9 @@ static void ImGui::NavUpdateWindowing() { // Move to parent menu if necessary ImGuiWindow* new_nav_window = g.NavWindow; - while ((new_nav_window->DC.NavLayerActiveMask & (1 << 1)) == 0 && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) + while ((new_nav_window->DC.NavLayerActiveMask & (1 << 1)) == 0 + && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 + && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) new_nav_window = new_nav_window->ParentWindow; if (new_nav_window != g.NavWindow) { @@ -7719,7 +8150,7 @@ static void ImGui::NavUpdateWindowing() } g.NavDisableHighlight = false; g.NavDisableMouseHover = true; - NavRestoreLayer((g.NavWindow->DC.NavLayerActiveMask & (1 << 1)) ? (g.NavLayer ^ 1) : 0); + NavRestoreLayer((g.NavWindow->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main); } } @@ -7774,10 +8205,18 @@ void ImGui::NextColumn() return; ImGuiContext& g = *GImGui; + ImGuiColumnsSet* columns = window->DC.ColumnsSet; + + if (columns->Count == 1) + { + window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); + IM_ASSERT(columns->Current == 0); + return; + } + PopItemWidth(); PopClipRect(); - ImGuiColumnsSet* columns = window->DC.ColumnsSet; columns->LineMaxY = ImMax(columns->LineMaxY, window->DC.CursorPos.y); if (++columns->Current < columns->Count) { @@ -7787,6 +8226,7 @@ void ImGui::NextColumn() } else { + // New line window->DC.ColumnsOffset.x = 0.0f; window->DrawList->ChannelsSetCurrent(0); columns->Current = 0; @@ -7941,7 +8381,7 @@ void ImGui::BeginColumns(const char* str_id, int columns_count, ImGuiColumnsFlag ImGuiContext& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); - IM_ASSERT(columns_count > 1); + IM_ASSERT(columns_count >= 1); IM_ASSERT(window->DC.ColumnsSet == NULL); // Nested columns are currently not supported // Differentiate column ID with an arbitrary prefix for cases where users name their columns set the same as another widget. @@ -7995,8 +8435,11 @@ void ImGui::BeginColumns(const char* str_id, int columns_count, ImGuiColumnsFlag column->ClipRect.ClipWith(window->ClipRect); } - window->DrawList->ChannelsSplit(columns->Count); - PushColumnClipRect(); + if (columns->Count > 1) + { + window->DrawList->ChannelsSplit(columns->Count); + PushColumnClipRect(); + } PushItemWidth(GetColumnWidth() * 0.65f); } @@ -8008,8 +8451,11 @@ void ImGui::EndColumns() IM_ASSERT(columns != NULL); PopItemWidth(); - PopClipRect(); - window->DrawList->ChannelsMerge(); + if (columns->Count > 1) + { + PopClipRect(); + window->DrawList->ChannelsMerge(); + } columns->LineMaxY = ImMax(columns->LineMaxY, window->DC.CursorPos.y); window->DC.CursorPos.y = columns->LineMaxY; @@ -8094,7 +8540,7 @@ void ImGui::ClearDragDrop() ImGuiContext& g = *GImGui; g.DragDropActive = false; g.DragDropPayload.Clear(); - g.DragDropAcceptFlags = 0; + g.DragDropAcceptFlags = ImGuiDragDropFlags_None; g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0; g.DragDropAcceptIdCurrRectSurface = FLT_MAX; g.DragDropAcceptFrameCount = -1; @@ -8162,7 +8608,7 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags) else { window = NULL; - source_id = ImHash("#SourceExtern", 0); + source_id = ImHashStr("#SourceExtern", 0); source_drag_active = true; } @@ -8185,9 +8631,9 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags) if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip)) { // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit) - // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents. + // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents. BeginTooltip(); - if (g.DragDropActive && g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip)) + if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip)) { ImGuiWindow* tooltip_window = g.CurrentWindow; tooltip_window->SkipItems = true; @@ -8378,9 +8824,13 @@ void ImGui::EndDragDropTarget() g.DragDropWithinSourceOrTarget = false; } + //----------------------------------------------------------------------------- // [SECTION] LOGGING/CAPTURING //----------------------------------------------------------------------------- +// All text output from the interface can be captured into tty/file/clipboard. +// By default, tree nodes are automatically opened during logging. +//----------------------------------------------------------------------------- // Pass text data straight to log (without being displayed) void ImGui::LogText(const char* fmt, ...) @@ -8394,7 +8844,7 @@ void ImGui::LogText(const char* fmt, ...) if (g.LogFile) vfprintf(g.LogFile, fmt, args); else - g.LogClipboard.appendfv(fmt, args); + g.LogBuffer.appendfv(fmt, args); va_end(args); } @@ -8408,17 +8858,20 @@ void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* if (!text_end) text_end = FindRenderedTextEnd(text, text_end); - const bool log_new_line = ref_pos && (ref_pos->y > window->DC.LogLinePosY + 1); + const bool log_new_line = ref_pos && (ref_pos->y > g.LogLinePosY + 1); if (ref_pos) - window->DC.LogLinePosY = ref_pos->y; + g.LogLinePosY = ref_pos->y; + if (log_new_line) + g.LogLineFirstItem = true; const char* text_remaining = text; - if (g.LogStartDepth > window->DC.TreeDepth) // Re-adjust padding if we have popped out of our starting depth - g.LogStartDepth = window->DC.TreeDepth; - const int tree_depth = (window->DC.TreeDepth - g.LogStartDepth); + if (g.LogDepthRef > window->DC.TreeDepth) // Re-adjust padding if we have popped out of our starting depth + g.LogDepthRef = window->DC.TreeDepth; + const int tree_depth = (window->DC.TreeDepth - g.LogDepthRef); for (;;) { // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry. + // We don't add a trailing \n to allow a subsequent item on the same line to be captured. const char* line_start = text_remaining; const char* line_end = ImStreolRange(line_start, text_end); const bool is_first_line = (line_start == text); @@ -8427,9 +8880,18 @@ void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* { const int char_count = (int)(line_end - line_start); if (log_new_line || !is_first_line) - LogText(IM_NEWLINE "%*s%.*s", tree_depth*4, "", char_count, line_start); - else + LogText(IM_NEWLINE "%*s%.*s", tree_depth * 4, "", char_count, line_start); + else if (g.LogLineFirstItem) + LogText("%*s%.*s", tree_depth * 4, "", char_count, line_start); + else LogText(" %.*s", char_count, line_start); + g.LogLineFirstItem = false; + } + else if (log_new_line) + { + // An empty "" string at a different Y position should output a carriage return. + LogText(IM_NEWLINE); + break; } if (is_last_line) @@ -8438,64 +8900,71 @@ void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* } } -// Start logging ImGui output to TTY -void ImGui::LogToTTY(int max_depth) +// Start logging/capturing text output +void ImGui::LogBegin(ImGuiLogType type, int auto_open_depth) { ImGuiContext& g = *GImGui; - if (g.LogEnabled) - return; ImGuiWindow* window = g.CurrentWindow; - + IM_ASSERT(g.LogEnabled == false); IM_ASSERT(g.LogFile == NULL); - g.LogFile = stdout; + IM_ASSERT(g.LogBuffer.empty()); g.LogEnabled = true; - g.LogStartDepth = window->DC.TreeDepth; - if (max_depth >= 0) - g.LogAutoExpandMaxDepth = max_depth; + g.LogType = type; + g.LogDepthRef = window->DC.TreeDepth; + g.LogDepthToExpand = ((auto_open_depth >= 0) ? auto_open_depth : g.LogDepthToExpandDefault); + g.LogLinePosY = FLT_MAX; + g.LogLineFirstItem = true; } -// Start logging ImGui output to given file -void ImGui::LogToFile(int max_depth, const char* filename) +void ImGui::LogToTTY(int auto_open_depth) +{ + ImGuiContext& g = *GImGui; + if (g.LogEnabled) + return; + LogBegin(ImGuiLogType_TTY, auto_open_depth); + g.LogFile = stdout; +} + +// Start logging/capturing text output to given file +void ImGui::LogToFile(int auto_open_depth, const char* filename) { ImGuiContext& g = *GImGui; if (g.LogEnabled) return; - ImGuiWindow* window = g.CurrentWindow; + // FIXME: We could probably open the file in text mode "at", however note that clipboard/buffer logging will still + // be subject to outputting OS-incompatible carriage return if within strings the user doesn't use IM_NEWLINE. + // By opening the file in binary mode "ab" we have consistent output everywhere. if (!filename) - { filename = g.IO.LogFilename; - if (!filename) - return; - } - - IM_ASSERT(g.LogFile == NULL); - g.LogFile = ImFileOpen(filename, "ab"); - if (!g.LogFile) + if (!filename || !filename[0]) + return; + FILE* f = ImFileOpen(filename, "ab"); + if (f == NULL) { - IM_ASSERT(g.LogFile != NULL); // Consider this an error + IM_ASSERT(0); return; } - g.LogEnabled = true; - g.LogStartDepth = window->DC.TreeDepth; - if (max_depth >= 0) - g.LogAutoExpandMaxDepth = max_depth; + + LogBegin(ImGuiLogType_File, auto_open_depth); + g.LogFile = f; } -// Start logging ImGui output to clipboard -void ImGui::LogToClipboard(int max_depth) +// Start logging/capturing text output to clipboard +void ImGui::LogToClipboard(int auto_open_depth) { ImGuiContext& g = *GImGui; if (g.LogEnabled) return; - ImGuiWindow* window = g.CurrentWindow; + LogBegin(ImGuiLogType_Clipboard, auto_open_depth); +} - IM_ASSERT(g.LogFile == NULL); - g.LogFile = NULL; - g.LogEnabled = true; - g.LogStartDepth = window->DC.TreeDepth; - if (max_depth >= 0) - g.LogAutoExpandMaxDepth = max_depth; +void ImGui::LogToBuffer(int auto_open_depth) +{ + ImGuiContext& g = *GImGui; + if (g.LogEnabled) + return; + LogBegin(ImGuiLogType_Buffer, auto_open_depth); } void ImGui::LogFinish() @@ -8505,23 +8974,33 @@ void ImGui::LogFinish() return; LogText(IM_NEWLINE); - if (g.LogFile != NULL) - { - if (g.LogFile == stdout) - fflush(g.LogFile); - else - fclose(g.LogFile); - g.LogFile = NULL; - } - if (g.LogClipboard.size() > 1) + switch (g.LogType) { - SetClipboardText(g.LogClipboard.begin()); - g.LogClipboard.clear(); + case ImGuiLogType_TTY: + fflush(g.LogFile); + break; + case ImGuiLogType_File: + fclose(g.LogFile); + break; + case ImGuiLogType_Buffer: + break; + case ImGuiLogType_Clipboard: + if (!g.LogBuffer.empty()) + SetClipboardText(g.LogBuffer.begin()); + break; + case ImGuiLogType_None: + IM_ASSERT(0); + break; } + g.LogEnabled = false; + g.LogType = ImGuiLogType_None; + g.LogFile = NULL; + g.LogBuffer.clear(); } // Helper to display logging buttons +// FIXME-OBSOLETE: We should probably obsolete this and let the user have their own helper (this is one of the oldest function alive!) void ImGui::LogButtons() { ImGuiContext& g = *GImGui; @@ -8532,18 +9011,18 @@ void ImGui::LogButtons() const bool log_to_clipboard = Button("Log To Clipboard"); SameLine(); PushItemWidth(80.0f); PushAllowKeyboardFocus(false); - SliderInt("Depth", &g.LogAutoExpandMaxDepth, 0, 9, NULL); + SliderInt("Default Depth", &g.LogDepthToExpandDefault, 0, 9, NULL); PopAllowKeyboardFocus(); PopItemWidth(); PopID(); // Start logging at the end of the function so that the buttons don't appear in the log if (log_to_tty) - LogToTTY(g.LogAutoExpandMaxDepth); + LogToTTY(); if (log_to_file) - LogToFile(g.LogAutoExpandMaxDepth, g.IO.LogFilename); + LogToFile(); if (log_to_clipboard) - LogToClipboard(g.LogAutoExpandMaxDepth); + LogToClipboard(); } //----------------------------------------------------------------------------- @@ -8571,7 +9050,7 @@ ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name) g.SettingsWindows.push_back(ImGuiWindowSettings()); ImGuiWindowSettings* settings = &g.SettingsWindows.back(); settings->Name = ImStrdup(name); - settings->ID = ImHash(name, 0); + settings->ID = ImHashStr(name, 0); return settings; } @@ -8584,6 +9063,13 @@ ImGuiWindowSettings* ImGui::FindWindowSettings(ImGuiID id) return NULL; } +ImGuiWindowSettings* ImGui::FindOrCreateWindowSettings(const char* name) +{ + if (ImGuiWindowSettings* settings = FindWindowSettings(ImHashStr(name, 0))) + return settings; + return CreateNewWindowSettings(name); +} + void ImGui::LoadIniSettingsFromDisk(const char* ini_filename) { size_t file_data_size = 0; @@ -8597,7 +9083,7 @@ void ImGui::LoadIniSettingsFromDisk(const char* ini_filename) ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name) { ImGuiContext& g = *GImGui; - const ImGuiID type_hash = ImHash(type_name, 0, 0); + const ImGuiID type_hash = ImHashStr(type_name, 0); for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) if (g.SettingsHandlers[handler_n].TypeHash == type_hash) return &g.SettingsHandlers[handler_n]; @@ -8701,7 +9187,7 @@ const char* ImGui::SaveIniSettingsToMemory(size_t* out_size) static void* SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name) { - ImGuiWindowSettings* settings = ImGui::FindWindowSettings(ImHash(name, 0)); + ImGuiWindowSettings* settings = ImGui::FindWindowSettings(ImHashStr(name, 0)); if (!settings) settings = ImGui::CreateNewWindowSettings(name); return (void*)settings; @@ -8732,7 +9218,7 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSetting if (!settings) { settings = ImGui::CreateNewWindowSettings(window->Name); - window->SettingsIdx = g.SettingsWindows.index_from_pointer(settings); + window->SettingsIdx = g.SettingsWindows.index_from_ptr(settings); } IM_ASSERT(settings->ID == window->ID); settings->Pos = window->Pos; @@ -8758,6 +9244,21 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSetting } } + +//----------------------------------------------------------------------------- +// [SECTION] VIEWPORTS, PLATFORM WINDOWS +//----------------------------------------------------------------------------- + +// (this section is filled in the 'docking' branch) + + +//----------------------------------------------------------------------------- +// [SECTION] DOCKING +//----------------------------------------------------------------------------- + +// (this section is filled in the 'docking' branch) + + //----------------------------------------------------------------------------- // [SECTION] PLATFORM DEPENDENT HELPERS //----------------------------------------------------------------------------- @@ -8885,6 +9386,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::End(); return; } + static bool show_draw_cmd_clip_rects = true; static bool show_window_begin_order = false; ImGuiIO& io = ImGui::GetIO(); @@ -8895,7 +9397,6 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::Text("%d allocations", io.MetricsActiveAllocations); ImGui::Checkbox("Show clipping rectangles when hovering draw commands", &show_draw_cmd_clip_rects); ImGui::Checkbox("Ctrl shows window begin order", &show_window_begin_order); - ImGui::Separator(); struct Funcs @@ -8906,14 +9407,14 @@ void ImGui::ShowMetricsWindow(bool* p_open) if (draw_list == ImGui::GetWindowDrawList()) { ImGui::SameLine(); - ImGui::TextColored(ImColor(255,100,100), "CURRENTLY APPENDING"); // Can't display stats for active draw list! (we don't have the data double-buffered) + ImGui::TextColored(ImVec4(1.0f,0.4f,0.4f,1.0f), "CURRENTLY APPENDING"); // Can't display stats for active draw list! (we don't have the data double-buffered) if (node_open) ImGui::TreePop(); return; } - ImDrawList* overlay_draw_list = GetOverlayDrawList(window); // Render additional visuals into the top-most draw list + ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list if (window && IsItemHovered()) - overlay_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255)); + fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255)); if (!node_open) return; @@ -8935,8 +9436,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImRect vtxs_rect; for (int i = elem_offset; i < elem_offset + (int)pcmd->ElemCount; i++) vtxs_rect.Add(draw_list->VtxBuffer[idx_buffer ? idx_buffer[i] : i].pos); - clip_rect.Floor(); overlay_draw_list->AddRect(clip_rect.Min, clip_rect.Max, IM_COL32(255,255,0,255)); - vtxs_rect.Floor(); overlay_draw_list->AddRect(vtxs_rect.Min, vtxs_rect.Max, IM_COL32(255,0,255,255)); + clip_rect.Floor(); fg_draw_list->AddRect(clip_rect.Min, clip_rect.Max, IM_COL32(255,255,0,255)); + vtxs_rect.Floor(); fg_draw_list->AddRect(vtxs_rect.Min, vtxs_rect.Max, IM_COL32(255,0,255,255)); } if (!pcmd_node_open) continue; @@ -8944,24 +9445,26 @@ void ImGui::ShowMetricsWindow(bool* p_open) // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted. ImGuiListClipper clipper(pcmd->ElemCount/3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible. while (clipper.Step()) - for (int prim = clipper.DisplayStart, vtx_i = elem_offset + clipper.DisplayStart*3; prim < clipper.DisplayEnd; prim++) + for (int prim = clipper.DisplayStart, idx_i = elem_offset + clipper.DisplayStart*3; prim < clipper.DisplayEnd; prim++) { char buf[300]; char *buf_p = buf, *buf_end = buf + IM_ARRAYSIZE(buf); ImVec2 triangles_pos[3]; - for (int n = 0; n < 3; n++, vtx_i++) + for (int n = 0; n < 3; n++, idx_i++) { - ImDrawVert& v = draw_list->VtxBuffer[idx_buffer ? idx_buffer[vtx_i] : vtx_i]; + int vtx_i = idx_buffer ? idx_buffer[idx_i] : idx_i; + ImDrawVert& v = draw_list->VtxBuffer[vtx_i]; triangles_pos[n] = v.pos; - buf_p += ImFormatString(buf_p, (int)(buf_end - buf_p), "%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n", (n == 0) ? "vtx" : " ", vtx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col); + buf_p += ImFormatString(buf_p, buf_end - buf_p, "%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n", + (n == 0) ? "idx" : " ", idx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col); } ImGui::Selectable(buf, false); if (ImGui::IsItemHovered()) { - ImDrawListFlags backup_flags = overlay_draw_list->Flags; - overlay_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines at is more readable for very large and thin triangles. - overlay_draw_list->AddPolyline(triangles_pos, 3, IM_COL32(255,255,0,255), true, 1.0f); - overlay_draw_list->Flags = backup_flags; + ImDrawListFlags backup_flags = fg_draw_list->Flags; + fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines at is more readable for very large and thin triangles. + fg_draw_list->AddPolyline(triangles_pos, 3, IM_COL32(255,255,0,255), true, 1.0f); + fg_draw_list->Flags = backup_flags; } } ImGui::TreePop(); @@ -9019,6 +9522,28 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::BulletText("Storage: %d bytes", window->StateStorage.Data.Size * (int)sizeof(ImGuiStorage::Pair)); ImGui::TreePop(); } + + static void NodeTabBar(ImGuiTabBar* tab_bar) + { + // Standalone tab bars (not associated to docking/windows functionality) currently hold no discernible strings. + char buf[256]; + char* p = buf; + const char* buf_end = buf + IM_ARRAYSIZE(buf); + ImFormatString(p, buf_end - p, "TabBar (%d tabs)%s", tab_bar->Tabs.Size, (tab_bar->PrevFrameVisible < ImGui::GetFrameCount() - 2) ? " *Inactive*" : ""); + if (ImGui::TreeNode(tab_bar, "%s", buf)) + { + for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) + { + const ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; + ImGui::PushID(tab); + if (ImGui::SmallButton("<")) { TabBarQueueChangeTabOrder(tab_bar, tab, -1); } ImGui::SameLine(0, 2); + if (ImGui::SmallButton(">")) { TabBarQueueChangeTabOrder(tab_bar, tab, +1); } ImGui::SameLine(); + ImGui::Text("%02d%c Tab 0x%08X", tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID); + ImGui::PopID(); + } + ImGui::TreePop(); + } + } }; // Access private state, we are going to display the draw lists from last frame @@ -9039,6 +9564,12 @@ void ImGui::ShowMetricsWindow(bool* p_open) } ImGui::TreePop(); } + if (ImGui::TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.Data.Size)) + { + for (int n = 0; n < g.TabBars.Data.Size; n++) + Funcs::NodeTabBar(g.TabBars.GetByIndex(n)); + ImGui::TreePop(); + } if (ImGui::TreeNode("Internal state")) { const char* input_source_names[] = { "None", "Mouse", "Nav", "NavKeyboard", "NavGamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT); @@ -9070,9 +9601,9 @@ void ImGui::ShowMetricsWindow(bool* p_open) char buf[32]; ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext); float font_size = ImGui::GetFontSize() * 2; - ImDrawList* overlay_draw_list = GetOverlayDrawList(window); - overlay_draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255)); - overlay_draw_list->AddText(NULL, font_size, window->Pos, IM_COL32(255, 255, 255, 255), buf); + ImDrawList* fg_draw_list = GetForegroundDrawList(window); + fg_draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255)); + fg_draw_list->AddText(NULL, font_size, window->Pos, IM_COL32(255, 255, 255, 255), buf); } } ImGui::End(); diff --git a/src/lib/geogram_gfx/third_party/ImGui/imgui.h b/src/lib/geogram_gfx/third_party/ImGui/imgui.h index ff9236d9..fc5d445f 100644 --- a/src/lib/geogram_gfx/third_party/ImGui/imgui.h +++ b/src/lib/geogram_gfx/third_party/ImGui/imgui.h @@ -1,14 +1,32 @@ -// dear imgui, v1.66 +// dear imgui, v1.69 // (headers) // See imgui.cpp file for documentation. // Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp for demo code. -// Read 'Programmer guide' in imgui.cpp for notes on how to setup ImGui in your codebase. +// Newcomers, read 'Programmer guide' in imgui.cpp for notes on how to setup Dear ImGui in your codebase. // Get latest version at https://github.com/ocornut/imgui +/* + +Index of this file: +// Header mess +// Forward declarations and basic types +// ImGui API (Dear ImGui end-user API) +// Flags & Enumerations +// ImVector<> +// ImGuiStyle +// ImGuiIO +// Misc data structures (ImGuiInputTextCallbackData, ImGuiSizeCallbackData, ImGuiPayload) +// Obsolete functions +// Helpers (ImGuiOnceUponAFrame, ImGuiTextFilter, ImGuiTextBuffer, ImGuiStorage, ImGuiListClipper, ImColor) +// Draw List API (ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListFlags, ImDrawList, ImDrawData) +// Font API (ImFontConfig, ImFontGlyph, ImFontGlyphRangesBuilder, ImFontAtlasFlags, ImFontAtlas, ImFont) + +*/ + #pragma once -// Configuration file (edit imconfig.h or define IMGUI_USER_CONFIG to set your own filename) +// Configuration file with compile-time options (edit imconfig.h or define IMGUI_USER_CONFIG to your own filename) #ifdef IMGUI_USER_CONFIG #include IMGUI_USER_CONFIG #endif @@ -16,15 +34,19 @@ #include "imconfig.h" #endif +//----------------------------------------------------------------------------- +// Header mess +//----------------------------------------------------------------------------- + #include // FLT_MAX #include // va_list #include // ptrdiff_t, NULL #include // memset, memmove, memcpy, strlen, strchr, strcpy, strcmp // Version -// (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY00 then bounced up to XYY01 when release tagging happens) -#define IMGUI_VERSION "1.66" -#define IMGUI_VERSION_NUM 16601 +// (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) +#define IMGUI_VERSION "1.69" +#define IMGUI_VERSION_NUM 16900 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert)) // Define attributes of all API symbols declarations (e.g. for DLL under Windows) @@ -36,7 +58,7 @@ #define IMGUI_IMPL_API IMGUI_API #endif -// Helpers +// Helper Macros #ifndef IM_ASSERT #include #define IM_ASSERT(_EXPR) assert(_EXPR) // You can override the default assert handler by editing imconfig.h @@ -50,10 +72,15 @@ #endif #define IM_ARRAYSIZE(_ARR) ((int)(sizeof(_ARR)/sizeof(*_ARR))) // Size of a static C-style array. Don't use on pointers! #define IM_OFFSETOF(_TYPE,_MEMBER) ((size_t)&(((_TYPE*)0)->_MEMBER)) // Offset of _MEMBER within _TYPE. Standardized as offsetof() in modern C++. +#define IM_UNUSED(_VAR) ((void)_VAR) // Used to silence "unused variable warnings". Often useful as asserts may be stripped out from final builds. +// Warnings #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wold-style-cast" +#if __has_warning("-Wzero-as-null-pointer-constant") +#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" +#endif #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" // [Bruno Levy] #pragma clang diagnostic ignored "-Wc++11-long-long" // [Bruno Levy] #elif defined(__GNUC__) && __GNUC__ >= 8 @@ -61,21 +88,23 @@ #pragma GCC diagnostic ignored "-Wclass-memaccess" #endif -// Forward declarations -struct ImDrawChannel; // Temporary storage for outputting drawing commands out of order, used by ImDrawList::ChannelsSplit() -struct ImDrawCmd; // A single draw command within a parent ImDrawList (generally maps to 1 GPU draw call) -struct ImDrawData; // All draw command lists required to render the frame +//----------------------------------------------------------------------------- +// Forward declarations and basic types +//----------------------------------------------------------------------------- + +struct ImDrawChannel; // Temporary storage for ImDrawList ot output draw commands out of order, used by ImDrawList::ChannelsSplit() +struct ImDrawCmd; // A single draw command within a parent ImDrawList (generally maps to 1 GPU draw call, unless it is a callback) +struct ImDrawData; // All draw command lists required to render the frame + pos/size coordinates to use for the projection matrix. struct ImDrawList; // A single draw command list (generally one per window, conceptually you may see this as a dynamic "mesh" builder) struct ImDrawListSharedData; // Data shared among multiple draw lists (typically owned by parent ImGui context, but you may create one yourself) -struct ImDrawVert; // A single vertex (20 bytes by default, override layout with IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT) +struct ImDrawVert; // A single vertex (pos + uv + col = 20 bytes by default. Override layout with IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT) struct ImFont; // Runtime data for a single font within a parent ImFontAtlas struct ImFontAtlas; // Runtime data for multiple fonts, bake multiple fonts into a single texture, TTF/OTF font loader struct ImFontConfig; // Configuration data when adding a font or merging fonts -struct ImColor; // Helper functions to create a color that can be converted to either u32 or float4 (*obsolete* please avoid using) -#ifndef ImTextureID -typedef void* ImTextureID; // User data to identify a texture (this is whatever to you want it to be! read the FAQ about ImTextureID in imgui.cpp) -#endif -struct ImGuiContext; // ImGui context (opaque) +struct ImFontGlyph; // A single font glyph (code point + coordinates within in ImFontAtlas + offset) +struct ImFontGlyphRangesBuilder; // Helper to build glyph ranges from text/string data +struct ImColor; // Helper functions to create a color that can be converted to either u32 or float4 (*OBSOLETE* please avoid using) +struct ImGuiContext; // Dear ImGui context (opaque structure, unless including imgui_internal.h) struct ImGuiIO; // Main configuration and I/O between your application and ImGui struct ImGuiInputTextCallbackData; // Shared state of InputText() when using custom ImGuiInputTextCallback (rare/advanced use) struct ImGuiListClipper; // Helper to manually clip large list of items @@ -84,13 +113,16 @@ struct ImGuiPayload; // User data payload for drag and drop opera struct ImGuiSizeCallbackData; // Callback data when using SetNextWindowSizeConstraints() (rare/advanced use) struct ImGuiStorage; // Helper for key->value storage struct ImGuiStyle; // Runtime data for styling/colors -struct ImGuiTextFilter; // Helper to parse and apply text filters (e.g. "aaaaa[,bbbb][,ccccc]") struct ImGuiTextBuffer; // Helper to hold and append into a text buffer (~string builder) +struct ImGuiTextFilter; // Helper to parse and apply text filters (e.g. "aaaaa[,bbbb][,ccccc]") // Typedefs and Enums/Flags (declared as int for compatibility with old C++, to allow using as flags and to not pollute the top of this file) // Use your programming IDE "Go to definition" facility on the names of the center columns to find the actual flags/enum lists. +#ifndef ImTextureID +typedef void* ImTextureID; // User data to identify a texture (this is whatever to you want it to be! read the FAQ about ImTextureID in imgui.cpp) +#endif typedef unsigned int ImGuiID; // Unique ID used by widgets (typically hashed from a stack of string) -typedef unsigned short ImWchar; // Character for keyboard input/display +typedef unsigned short ImWchar; // A single U16 character for keyboard input/display. We encode them as multi bytes UTF-8 when used in strings. typedef int ImGuiCol; // -> enum ImGuiCol_ // Enum: A color identifier for styling typedef int ImGuiCond; // -> enum ImGuiCond_ // Enum: A condition for Set*() typedef int ImGuiDataType; // -> enum ImGuiDataType_ // Enum: A primary data type @@ -112,12 +144,18 @@ typedef int ImGuiFocusedFlags; // -> enum ImGuiFocusedFlags_ // Flags: f typedef int ImGuiHoveredFlags; // -> enum ImGuiHoveredFlags_ // Flags: for IsItemHovered(), IsWindowHovered() etc. typedef int ImGuiInputTextFlags; // -> enum ImGuiInputTextFlags_ // Flags: for InputText*() typedef int ImGuiSelectableFlags; // -> enum ImGuiSelectableFlags_ // Flags: for Selectable() +typedef int ImGuiTabBarFlags; // -> enum ImGuiTabBarFlags_ // Flags: for BeginTabBar() +typedef int ImGuiTabItemFlags; // -> enum ImGuiTabItemFlags_ // Flags: for BeginTabItem() typedef int ImGuiTreeNodeFlags; // -> enum ImGuiTreeNodeFlags_ // Flags: for TreeNode*(),CollapsingHeader() typedef int ImGuiWindowFlags; // -> enum ImGuiWindowFlags_ // Flags: for Begin*() typedef int (*ImGuiInputTextCallback)(ImGuiInputTextCallbackData *data); typedef void (*ImGuiSizeCallback)(ImGuiSizeCallbackData* data); // Scalar data types +typedef signed char ImS8; // 8-bit signed integer == char +typedef unsigned char ImU8; // 8-bit unsigned integer +typedef signed short ImS16; // 16-bit signed integer +typedef unsigned short ImU16; // 16-bit unsigned integer typedef signed int ImS32; // 32-bit signed integer == int typedef unsigned int ImU32; // 32-bit unsigned integer (often used to store packed colors) #if defined(_MSC_VER) && !defined(__clang__) @@ -138,7 +176,8 @@ struct ImVec2 float x, y; ImVec2() { x = y = 0.0f; } ImVec2(float _x, float _y) { x = _x; y = _y; } - float operator[] (size_t i) const { IM_ASSERT(i <= 1); return (&x)[i]; } // We very rarely use this [] operator, the assert overhead is fine. + float operator[] (size_t idx) const { IM_ASSERT(idx <= 1); return (&x)[idx]; } // We very rarely use this [] operator, the assert overhead is fine. + float& operator[] (size_t idx) { IM_ASSERT(idx <= 1); return (&x)[idx]; } // We very rarely use this [] operator, the assert overhead is fine. #ifdef IM_VEC2_CLASS_EXTRA IM_VEC2_CLASS_EXTRA // Define additional constructors and implicit cast operators in imconfig.h to convert back and forth between your math types and ImVec2. #endif @@ -155,8 +194,11 @@ struct ImVec4 #endif }; -// Dear ImGui end-user API -// (In a namespace so you can add extra functions in your own separate file. Please don't modify imgui.cpp/.h!) +//----------------------------------------------------------------------------- +// ImGui: Dear ImGui end-user API +// (Inside a namespace so you can add extra functions in your own separate file. Please don't modify imgui.cpp/.h!) +//----------------------------------------------------------------------------- + namespace ImGui { // Context creation and access @@ -178,12 +220,13 @@ namespace ImGui // Demo, Debug, Information IMGUI_API void ShowDemoWindow(bool* p_open = NULL); // create demo/test window (previously called ShowTestWindow). demonstrate most ImGui features. call this to learn about the library! try to make it always available in your application! - IMGUI_API void ShowMetricsWindow(bool* p_open = NULL); // create metrics window. display ImGui internals: draw commands (with individual draw calls and vertices), window list, basic internal state, etc. + IMGUI_API void ShowAboutWindow(bool* p_open = NULL); // create about window. display Dear ImGui version, credits and build/system information. + IMGUI_API void ShowMetricsWindow(bool* p_open = NULL); // create metrics/debug window. display Dear ImGui internals: draw commands (with individual draw calls and vertices), window list, basic internal state, etc. IMGUI_API void ShowStyleEditor(ImGuiStyle* ref = NULL); // add style editor block (not a window). you can pass in a reference ImGuiStyle structure to compare to, revert to and save to (else it uses the default style) IMGUI_API bool ShowStyleSelector(const char* label); // add style selector block (not a window), essentially a combo listing the default styles. IMGUI_API void ShowFontSelector(const char* label); // add font selector block (not a window), essentially a combo listing the loaded fonts. IMGUI_API void ShowUserGuide(); // add basic help/info block (not a window): how to manipulate ImGui as a end-user (mouse/keyboard controls). - IMGUI_API const char* GetVersion(); // get the compiled version string e.g. "1.23" + IMGUI_API const char* GetVersion(); // get the compiled version string e.g. "1.23" (essentially the compiled value for IMGUI_VERSION) // Styles IMGUI_API void StyleColorsDark(ImGuiStyle* dst = NULL); // new, recommended style (default) @@ -193,9 +236,13 @@ namespace ImGui // Windows // - Begin() = push window to the stack and start appending to it. End() = pop window from the stack. // - You may append multiple times to the same window during the same frame. - // - Passing 'bool* p_open != NULL' shows a window-closing widget in the upper-right corner of the window, which clicking will set the boolean to false when clicked. - // - Begin() return false to indicate the window is collapsed or fully clipped, so you may early out and omit submitting anything to the window. - // Always call a matching End() for each Begin() call, regardless of its return value [this is due to legacy reason and is inconsistent with most other functions such as BeginMenu/EndMenu, BeginPopup/EndPopup, etc. where the EndXXX call should only be called if the corresponding BeginXXX function returned true.] + // - Passing 'bool* p_open != NULL' shows a window-closing widget in the upper-right corner of the window, + // which clicking will set the boolean to false when clicked. + // - Begin() return false to indicate the window is collapsed or fully clipped, so you may early out and omit submitting + // anything to the window. Always call a matching End() for each Begin() call, regardless of its return value! + // [this is due to legacy reason and is inconsistent with most other functions such as BeginMenu/EndMenu, BeginPopup/EndPopup, etc. + // where the EndXXX call should only be called if the corresponding BeginXXX function returned true.] + // - Note that the bottom of window stack always contains a window called "Debug". IMGUI_API bool Begin(const char* name, bool* p_open = NULL, ImGuiWindowFlags flags = 0); IMGUI_API void End(); @@ -209,18 +256,20 @@ namespace ImGui IMGUI_API void EndChild(); // Windows Utilities + // - "current window" = the window we are appending into while inside a Begin()/End() block. "next window" = next window we will Begin() into. IMGUI_API bool IsWindowAppearing(); IMGUI_API bool IsWindowCollapsed(); IMGUI_API bool IsWindowFocused(ImGuiFocusedFlags flags=0); // is current window focused? or its root/child, depending on flags. see flags for options. IMGUI_API bool IsWindowHovered(ImGuiHoveredFlags flags=0); // is current window hovered (and typically: not blocked by a popup/modal)? see flags for options. NB: If you are trying to check whether your mouse should be dispatched to imgui or to your app, you should use the 'io.WantCaptureMouse' boolean for that! Please read the FAQ! - IMGUI_API ImDrawList* GetWindowDrawList(); // get draw list associated to the window, to append your own drawing primitives + IMGUI_API ImDrawList* GetWindowDrawList(); // get draw list associated to the current window, to append your own drawing primitives + IMGUI_API ImVec2 GetWindowPos(); // get current window position in screen space (useful if you want to do your own drawing via the DrawList API) IMGUI_API ImVec2 GetWindowSize(); // get current window size IMGUI_API float GetWindowWidth(); // get current window width (shortcut for GetWindowSize().x) IMGUI_API float GetWindowHeight(); // get current window height (shortcut for GetWindowSize().y) IMGUI_API ImVec2 GetContentRegionMax(); // current content boundaries (typically window boundaries including scrolling, or current column boundaries), in windows coordinates IMGUI_API ImVec2 GetContentRegionAvail(); // == GetContentRegionMax() - GetCursorPos() - IMGUI_API float GetContentRegionAvailWidth(); // + IMGUI_API float GetContentRegionAvailWidth(); // == GetContentRegionAvail().x IMGUI_API ImVec2 GetWindowContentRegionMin(); // content boundaries min (roughly (0,0)-Scroll), in window coordinates IMGUI_API ImVec2 GetWindowContentRegionMax(); // content boundaries max (roughly (0,0)+Size-Scroll) where Size can be override with SetNextWindowContentSize(), in window coordinates IMGUI_API float GetWindowContentRegionWidth(); // @@ -249,8 +298,8 @@ namespace ImGui IMGUI_API float GetScrollMaxY(); // get maximum scrolling amount ~~ ContentSize.Y - WindowSize.Y IMGUI_API void SetScrollX(float scroll_x); // set scrolling amount [0..GetScrollMaxX()] IMGUI_API void SetScrollY(float scroll_y); // set scrolling amount [0..GetScrollMaxY()] - IMGUI_API void SetScrollHereY(float center_y_ratio = 0.5f); // adjust scrolling amount to make current cursor position visible. center_y_ratio=0.0: top, 0.5: center, 1.0: bottom. When using to make a "default/current item" visible, consider using SetItemDefaultFocus() instead. - IMGUI_API void SetScrollFromPosY(float pos_y, float center_y_ratio = 0.5f); // adjust scrolling amount to make given position valid. use GetCursorPos() or GetCursorStartPos()+offset to get valid positions. + IMGUI_API void SetScrollHereY(float center_y_ratio = 0.5f); // adjust scrolling amount to make current cursor position visible. center_y_ratio=0.0: top, 0.5: center, 1.0: bottom. When using to make a "default/current item" visible, consider using SetItemDefaultFocus() instead. + IMGUI_API void SetScrollFromPosY(float local_y, float center_y_ratio = 0.5f); // adjust scrolling amount to make given position visible. Generally GetCursorStartPos() + offset to compute a valid position. // Parameters stacks (shared) IMGUI_API void PushFont(ImFont* font); // use NULL as a shortcut to push default font @@ -273,7 +322,7 @@ namespace ImGui IMGUI_API void PushItemWidth(float item_width); // width of items for the common item+label case, pixels. 0.0f = default to ~2/3 of windows width, >0.0f: width in pixels, <0.0f align xx pixels to the right of window (so -1.0f always align width to the right side) IMGUI_API void PopItemWidth(); IMGUI_API float CalcItemWidth(); // width of item given pushed settings and current cursor position - IMGUI_API void PushTextWrapPos(float wrap_pos_x = 0.0f); // word-wrapping for Text*() commands. < 0.0f: no wrapping; 0.0f: wrap to end of window (or column); > 0.0f: wrap at 'wrap_pos_x' position in window local space + IMGUI_API void PushTextWrapPos(float wrap_local_pos_x = 0.0f); // word-wrapping for Text*() commands. < 0.0f: no wrapping; 0.0f: wrap to end of window (or column); > 0.0f: wrap at 'wrap_pos_x' position in window local space IMGUI_API void PopTextWrapPos(); IMGUI_API void PushAllowKeyboardFocus(bool allow_keyboard_focus); // allow focusing using TAB/Shift-TAB, enabled by default but you can disable it for certain widgets IMGUI_API void PopAllowKeyboardFocus(); @@ -281,24 +330,26 @@ namespace ImGui IMGUI_API void PopButtonRepeat(); // Cursor / Layout + // - By "cursor" we mean the current output position. + // - The typical widget behavior is to output themselves at the current cursor position, then move the cursor one line down. IMGUI_API void Separator(); // separator, generally horizontal. inside a menu bar or in horizontal layout mode, this becomes a vertical separator. - IMGUI_API void SameLine(float pos_x = 0.0f, float spacing_w = -1.0f); // call between widgets or groups to layout them horizontally - IMGUI_API void NewLine(); // undo a SameLine() - IMGUI_API void Spacing(); // add vertical spacing - IMGUI_API void Dummy(const ImVec2& size); // add a dummy item of given size + IMGUI_API void SameLine(float local_pos_x = 0.0f, float spacing_w = -1.0f); // call between widgets or groups to layout them horizontally. X position given in window coordinates. + IMGUI_API void NewLine(); // undo a SameLine() or force a new line when in an horizontal-layout context. + IMGUI_API void Spacing(); // add vertical spacing. + IMGUI_API void Dummy(const ImVec2& size); // add a dummy item of given size. unlike InvisibleButton(), Dummy() won't take the mouse click or be navigable into. IMGUI_API void Indent(float indent_w = 0.0f); // move content position toward the right, by style.IndentSpacing or indent_w if != 0 IMGUI_API void Unindent(float indent_w = 0.0f); // move content position back to the left, by style.IndentSpacing or indent_w if != 0 - IMGUI_API void BeginGroup(); // lock horizontal starting position + capture group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.) - IMGUI_API void EndGroup(); - IMGUI_API ImVec2 GetCursorPos(); // cursor position is relative to window position - IMGUI_API float GetCursorPosX(); // " - IMGUI_API float GetCursorPosY(); // " - IMGUI_API void SetCursorPos(const ImVec2& local_pos); // " - IMGUI_API void SetCursorPosX(float x); // " - IMGUI_API void SetCursorPosY(float y); // " - IMGUI_API ImVec2 GetCursorStartPos(); // initial cursor position + IMGUI_API void BeginGroup(); // lock horizontal starting position + IMGUI_API void EndGroup(); // unlock horizontal starting position + capture the whole group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.) + IMGUI_API ImVec2 GetCursorPos(); // cursor position in window coordinates (relative to window position) + IMGUI_API float GetCursorPosX(); // (some functions are using window-relative coordinates, such as: GetCursorPos, GetCursorStartPos, GetContentRegionMax, GetWindowContentRegion* etc. + IMGUI_API float GetCursorPosY(); // other functions such as GetCursorScreenPos or everything in ImDrawList:: + IMGUI_API void SetCursorPos(const ImVec2& local_pos); // are using the main, absolute coordinate system. + IMGUI_API void SetCursorPosX(float local_x); // GetWindowPos() + GetCursorPos() == GetCursorScreenPos() etc.) + IMGUI_API void SetCursorPosY(float local_y); // + IMGUI_API ImVec2 GetCursorStartPos(); // initial cursor position in window coordinates IMGUI_API ImVec2 GetCursorScreenPos(); // cursor position in absolute screen coordinates [0..io.DisplaySize] (useful to work with ImDrawList API) - IMGUI_API void SetCursorScreenPos(const ImVec2& screen_pos); // cursor position in absolute screen coordinates [0..io.DisplaySize] + IMGUI_API void SetCursorScreenPos(const ImVec2& pos); // cursor position in absolute screen coordinates [0..io.DisplaySize] IMGUI_API void AlignTextToFramePadding(); // vertically align upcoming text baseline to FramePadding.y so that it will align properly to regularly framed items (call if you have text on a line before a framed item) IMGUI_API float GetTextLineHeight(); // ~ FontSize IMGUI_API float GetTextLineHeightWithSpacing(); // ~ FontSize + style.ItemSpacing.y (distance in pixels between 2 consecutive lines of text) @@ -306,16 +357,17 @@ namespace ImGui IMGUI_API float GetFrameHeightWithSpacing(); // ~ FontSize + style.FramePadding.y * 2 + style.ItemSpacing.y (distance in pixels between 2 consecutive lines of framed widgets) // ID stack/scopes - // Read the FAQ for more details about how ID are handled in dear imgui. If you are creating widgets in a loop you most - // likely want to push a unique identifier (e.g. object pointer, loop index) to uniquely differentiate them. - // You can also use the "##foobar" syntax within widget label to distinguish them from each others. - // In this header file we use the "label"/"name" terminology to denote a string that will be displayed and used as an ID, - // whereas "str_id" denote a string that is only used as an ID and not aimed to be displayed. - IMGUI_API void PushID(const char* str_id); // push identifier into the ID stack. IDs are hash of the entire stack! - IMGUI_API void PushID(const char* str_id_begin, const char* str_id_end); - IMGUI_API void PushID(const void* ptr_id); - IMGUI_API void PushID(int int_id); - IMGUI_API void PopID(); + // - Read the FAQ for more details about how ID are handled in dear imgui. If you are creating widgets in a loop you most + // likely want to push a unique identifier (e.g. object pointer, loop index) to uniquely differentiate them. + // - The resulting ID are hashes of the entire stack. + // - You can also use the "Label##foobar" syntax within widget label to distinguish them from each others. + // - In this header file we use the "label"/"name" terminology to denote a string that will be displayed and used as an ID, + // whereas "str_id" denote a string that is only used as an ID and not normally displayed. + IMGUI_API void PushID(const char* str_id); // push string into the ID stack (will hash string). + IMGUI_API void PushID(const char* str_id_begin, const char* str_id_end); // push string into the ID stack (will hash string). + IMGUI_API void PushID(const void* ptr_id); // push pointer into the ID stack (will hash pointer). + IMGUI_API void PushID(int int_id); // push integer into the ID stack (will hash integer). + IMGUI_API void PopID(); // pop from the ID stack. IMGUI_API ImGuiID GetID(const char* str_id); // calculate unique ID (hash of whole ID stack + given parameter). e.g. if you want to query into ImGuiStorage yourself IMGUI_API ImGuiID GetID(const char* str_id_begin, const char* str_id_end); IMGUI_API ImGuiID GetID(const void* ptr_id); @@ -336,10 +388,10 @@ namespace ImGui IMGUI_API void BulletTextV(const char* fmt, va_list args) IM_FMTLIST(1); // Widgets: Main - // Most widgets return true when the value has been changed or when pressed/selected + // - Most widgets return true when the value has been changed or when pressed/selected IMGUI_API bool Button(const char* label, const ImVec2& size = ImVec2(0,0)); // button IMGUI_API bool SmallButton(const char* label); // button with FramePadding=(0,0) to easily embed within text - IMGUI_API bool InvisibleButton(const char* str_id, const ImVec2& size); // button behavior without the visuals, useful to build custom behaviors using the public api (along with IsItemActive, IsItemHovered, etc.) + IMGUI_API bool InvisibleButton(const char* str_id, const ImVec2& size); // button behavior without the visuals, frequently useful to build custom behaviors using the public api (along with IsItemActive, IsItemHovered, etc.) IMGUI_API bool ArrowButton(const char* str_id, ImGuiDir dir); // square button with an arrow shape IMGUI_API void Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0,0), const ImVec2& uv1 = ImVec2(1,1), const ImVec4& tint_col = ImVec4(1,1,1,1), const ImVec4& border_col = ImVec4(0,0,0,0)); IMGUI_API bool ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0,0), const ImVec2& uv1 = ImVec2(1,1), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0,0,0,0), const ImVec4& tint_col = ImVec4(1,1,1,1)); // <0 frame_padding uses default frame padding settings. 0 for no padding @@ -351,18 +403,19 @@ namespace ImGui IMGUI_API void Bullet(); // draw a small circle and keep the cursor on the same line. advance cursor x position by GetTreeNodeToLabelSpacing(), same distance that TreeNode() uses // Widgets: Combo Box - // The new BeginCombo()/EndCombo() api allows you to manage your contents and selection state however you want it, by creating e.g. Selectable() items. - // The old Combo() api are helpers over BeginCombo()/EndCombo() which are kept available for convenience purpose. + // - The new BeginCombo()/EndCombo() api allows you to manage your contents and selection state however you want it, by creating e.g. Selectable() items. + // - The old Combo() api are helpers over BeginCombo()/EndCombo() which are kept available for convenience purpose. IMGUI_API bool BeginCombo(const char* label, const char* preview_value, ImGuiComboFlags flags = 0); IMGUI_API void EndCombo(); // only call EndCombo() if BeginCombo() returns true! IMGUI_API bool Combo(const char* label, int* current_item, const char* const items[], int items_count, int popup_max_height_in_items = -1); IMGUI_API bool Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int popup_max_height_in_items = -1); // Separate items with \0 within a string, end item-list with \0\0. e.g. "One\0Two\0Three\0" IMGUI_API bool Combo(const char* label, int* current_item, bool(*items_getter)(void* data, int idx, const char** out_text), void* data, int items_count, int popup_max_height_in_items = -1); - // Widgets: Drags (tip: ctrl+click on a drag box to input with keyboard. manually input values aren't clamped, can go off-bounds) - // For all the Float2/Float3/Float4/Int2/Int3/Int4 versions of every functions, note that a 'float v[X]' function argument is the same as 'float* v', the array syntax is just a way to document the number of elements that are expected to be accessible. You can pass address of your first element out of a contiguous set, e.g. &myvector.x - // Adjust format string to decorate the value with a prefix, a suffix, or adapt the editing and display precision e.g. "%.3f" -> 1.234; "%5.2f secs" -> 01.23 secs; "Biscuit: %.0f" -> Biscuit: 1; etc. - // Speed are per-pixel of mouse movement (v_speed=0.2f: mouse needs to move by 5 pixels to increase value by 1). For gamepad/keyboard navigation, minimum speed is Max(v_speed, minimum_step_at_given_precision). + // Widgets: Drags + // - CTRL+Click on any drag box to turn them into an input box. Manually input values aren't clamped and can go off-bounds. + // - For all the Float2/Float3/Float4/Int2/Int3/Int4 versions of every functions, note that a 'float v[X]' function argument is the same as 'float* v', the array syntax is just a way to document the number of elements that are expected to be accessible. You can pass address of your first element out of a contiguous set, e.g. &myvector.x + // - Adjust format string to decorate the value with a prefix, a suffix, or adapt the editing and display precision e.g. "%.3f" -> 1.234; "%5.2f secs" -> 01.23 secs; "Biscuit: %.0f" -> Biscuit: 1; etc. + // - Speed are per-pixel of mouse movement (v_speed=0.2f: mouse needs to move by 5 pixels to increase value by 1). For gamepad/keyboard navigation, minimum speed is Max(v_speed, minimum_step_at_given_precision). IMGUI_API bool DragFloat(const char* label, float* v, float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", float power = 1.0f); // If v_min >= v_max we have no bound IMGUI_API bool DragFloat2(const char* label, float v[2], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", float power = 1.0f); IMGUI_API bool DragFloat3(const char* label, float v[3], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", float power = 1.0f); @@ -376,8 +429,9 @@ namespace ImGui IMGUI_API bool DragScalar(const char* label, ImGuiDataType data_type, void* v, float v_speed, const void* v_min = NULL, const void* v_max = NULL, const char* format = NULL, float power = 1.0f); IMGUI_API bool DragScalarN(const char* label, ImGuiDataType data_type, void* v, int components, float v_speed, const void* v_min = NULL, const void* v_max = NULL, const char* format = NULL, float power = 1.0f); - // Widgets: Sliders (tip: ctrl+click on a slider to input with keyboard. manually input values aren't clamped, can go off-bounds) - // Adjust format string to decorate the value with a prefix, a suffix, or adapt the editing and display precision e.g. "%.3f" -> 1.234; "%5.2f secs" -> 01.23 secs; "Biscuit: %.0f" -> Biscuit: 1; etc. + // Widgets: Sliders + // - CTRL+Click on any slider to turn them into an input box. Manually input values aren't clamped and can go off-bounds. + // - Adjust format string to decorate the value with a prefix, a suffix, or adapt the editing and display precision e.g. "%.3f" -> 1.234; "%5.2f secs" -> 01.23 secs; "Biscuit: %.0f" -> Biscuit: 1; etc. IMGUI_API bool SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f); // adjust format to decorate the value with a prefix or a suffix for in-slider labels or unit display. Use power!=1.0 for power curve sliders IMGUI_API bool SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format = "%.3f", float power = 1.0f); IMGUI_API bool SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format = "%.3f", float power = 1.0f); @@ -394,23 +448,26 @@ namespace ImGui IMGUI_API bool VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType data_type, void* v, const void* v_min, const void* v_max, const char* format = NULL, float power = 1.0f); // Widgets: Input with Keyboard - // If you want to use InputText() with a dynamic string type such as std::string or your own, see misc/cpp/imgui_stdlib.h + // - If you want to use InputText() with a dynamic string type such as std::string or your own, see misc/cpp/imgui_stdlib.h + // - Most of the ImGuiInputTextFlags flags are only useful for InputText() and not for InputFloatX, InputIntX, InputDouble etc. IMGUI_API bool InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); IMGUI_API bool InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size = ImVec2(0,0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); - IMGUI_API bool InputFloat(const char* label, float* v, float step = 0.0f, float step_fast = 0.0f, const char* format = "%.3f", ImGuiInputTextFlags extra_flags = 0); - IMGUI_API bool InputFloat2(const char* label, float v[2], const char* format = "%.3f", ImGuiInputTextFlags extra_flags = 0); - IMGUI_API bool InputFloat3(const char* label, float v[3], const char* format = "%.3f", ImGuiInputTextFlags extra_flags = 0); - IMGUI_API bool InputFloat4(const char* label, float v[4], const char* format = "%.3f", ImGuiInputTextFlags extra_flags = 0); - IMGUI_API bool InputInt(const char* label, int* v, int step = 1, int step_fast = 100, ImGuiInputTextFlags extra_flags = 0); - IMGUI_API bool InputInt2(const char* label, int v[2], ImGuiInputTextFlags extra_flags = 0); - IMGUI_API bool InputInt3(const char* label, int v[3], ImGuiInputTextFlags extra_flags = 0); - IMGUI_API bool InputInt4(const char* label, int v[4], ImGuiInputTextFlags extra_flags = 0); - IMGUI_API bool InputDouble(const char* label, double* v, double step = 0.0, double step_fast = 0.0, const char* format = "%.6f", ImGuiInputTextFlags extra_flags = 0); - IMGUI_API bool InputScalar(const char* label, ImGuiDataType data_type, void* v, const void* step = NULL, const void* step_fast = NULL, const char* format = NULL, ImGuiInputTextFlags extra_flags = 0); - IMGUI_API bool InputScalarN(const char* label, ImGuiDataType data_type, void* v, int components, const void* step = NULL, const void* step_fast = NULL, const char* format = NULL, ImGuiInputTextFlags extra_flags = 0); + IMGUI_API bool InputTextWithHint(const char* label, const char* hint, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); + IMGUI_API bool InputFloat(const char* label, float* v, float step = 0.0f, float step_fast = 0.0f, const char* format = "%.3f", ImGuiInputTextFlags flags = 0); + IMGUI_API bool InputFloat2(const char* label, float v[2], const char* format = "%.3f", ImGuiInputTextFlags flags = 0); + IMGUI_API bool InputFloat3(const char* label, float v[3], const char* format = "%.3f", ImGuiInputTextFlags flags = 0); + IMGUI_API bool InputFloat4(const char* label, float v[4], const char* format = "%.3f", ImGuiInputTextFlags flags = 0); + IMGUI_API bool InputInt(const char* label, int* v, int step = 1, int step_fast = 100, ImGuiInputTextFlags flags = 0); + IMGUI_API bool InputInt2(const char* label, int v[2], ImGuiInputTextFlags flags = 0); + IMGUI_API bool InputInt3(const char* label, int v[3], ImGuiInputTextFlags flags = 0); + IMGUI_API bool InputInt4(const char* label, int v[4], ImGuiInputTextFlags flags = 0); + IMGUI_API bool InputDouble(const char* label, double* v, double step = 0.0, double step_fast = 0.0, const char* format = "%.6f", ImGuiInputTextFlags flags = 0); + IMGUI_API bool InputScalar(const char* label, ImGuiDataType data_type, void* v, const void* step = NULL, const void* step_fast = NULL, const char* format = NULL, ImGuiInputTextFlags flags = 0); + IMGUI_API bool InputScalarN(const char* label, ImGuiDataType data_type, void* v, int components, const void* step = NULL, const void* step_fast = NULL, const char* format = NULL, ImGuiInputTextFlags flags = 0); // Widgets: Color Editor/Picker (tip: the ColorEdit* functions have a little colored preview square that can be left-clicked to open a picker, and right-clicked to open an option menu.) - // Note that a 'float v[X]' function argument is the same as 'float* v', the array syntax is just a way to document the number of elements that are expected to be accessible. You can the pass the address of a first float element out of a contiguous structure, e.g. &myvector.x + // - Note that in C++ a 'float v[X]' function argument is the _same_ as 'float* v', the array syntax is just a way to document the number of elements that are expected to be accessible. + // - You can pass the address of a first float element out of a contiguous structure, e.g. &myvector.x IMGUI_API bool ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flags = 0); IMGUI_API bool ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags = 0); IMGUI_API bool ColorPicker3(const char* label, float col[3], ImGuiColorEditFlags flags = 0); @@ -419,9 +476,9 @@ namespace ImGui IMGUI_API void SetColorEditOptions(ImGuiColorEditFlags flags); // initialize current options (generally on application startup) if you want to select a default format, picker type, etc. User will be able to change many settings, unless you pass the _NoOptions flag to your calls. // Widgets: Trees - // TreeNode functions return true when the node is open, in which case you need to also call TreePop() when you are finished displaying the tree node contents. + // - TreeNode functions return true when the node is open, in which case you need to also call TreePop() when you are finished displaying the tree node contents. IMGUI_API bool TreeNode(const char* label); - IMGUI_API bool TreeNode(const char* str_id, const char* fmt, ...) IM_FMTARGS(2); // helper variation to completely decorelate the id from the displayed string. Read the FAQ about why and how to use ID. to align arbitrary text at the same level as a TreeNode() you can use Bullet(). + IMGUI_API bool TreeNode(const char* str_id, const char* fmt, ...) IM_FMTARGS(2); // helper variation to easily decorelate the id from the displayed string. Read the FAQ about why and how to use ID. to align arbitrary text at the same level as a TreeNode() you can use Bullet(). IMGUI_API bool TreeNode(const void* ptr_id, const char* fmt, ...) IM_FMTARGS(2); // " IMGUI_API bool TreeNodeV(const char* str_id, const char* fmt, va_list args) IM_FMTLIST(2); IMGUI_API bool TreeNodeV(const void* ptr_id, const char* fmt, va_list args) IM_FMTLIST(2); @@ -440,10 +497,13 @@ namespace ImGui IMGUI_API bool CollapsingHeader(const char* label, bool* p_open, ImGuiTreeNodeFlags flags = 0); // when 'p_open' isn't NULL, display an additional small close button on upper right of the header // Widgets: Selectables + // - A selectable highlights when hovered, and can display another color when selected. + // - Neighbors selectable extend their highlight bounds in order to leave no gap between them. IMGUI_API bool Selectable(const char* label, bool selected = false, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0,0)); // "bool selected" carry the selection state (read-only). Selectable() is clicked is returns true so you can modify your selection state. size.x==0.0: use remaining width, size.x>0.0: specify width. size.y==0.0: use label height, size.y>0.0: specify height IMGUI_API bool Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0,0)); // "bool* p_selected" point to the selection state (read-write), as a convenient helper. // Widgets: List Boxes + // - FIXME: To be consistent with all the newer API, ListBoxHeader/ListBoxFooter should in reality be called BeginListBox/EndListBox. Will rename them. IMGUI_API bool ListBox(const char* label, int* current_item, const char* const items[], int items_count, int height_in_items = -1); IMGUI_API bool ListBox(const char* label, int* current_item, bool (*items_getter)(void* data, int idx, const char** out_text), void* data, int items_count, int height_in_items = -1); IMGUI_API bool ListBoxHeader(const char* label, const ImVec2& size = ImVec2(0,0)); // use if you want to reimplement ListBox() will custom data or interactions. if the function return true, you can output elements then call ListBoxFooter() afterwards. @@ -456,7 +516,8 @@ namespace ImGui IMGUI_API void PlotHistogram(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0), int stride = sizeof(float)); IMGUI_API void PlotHistogram(const char* label, float(*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0)); - // Widgets: Value() Helpers. Output single value in "name: value" format (tip: freely declare more in your code to handle your types. you can add functions to the ImGui namespace) + // Widgets: Value() Helpers. + // - Those are merely shortcut to calling Text() with a format string. Output single value in "name: value" format (tip: freely declare more in your code to handle your types. you can add functions to the ImGui namespace) IMGUI_API void Value(const char* prefix, bool b); IMGUI_API void Value(const char* prefix, int v); IMGUI_API void Value(const char* prefix, unsigned int v); @@ -475,10 +536,17 @@ namespace ImGui // Tooltips IMGUI_API void BeginTooltip(); // begin/append a tooltip window. to create full-featured tooltip (with any kind of items). IMGUI_API void EndTooltip(); - IMGUI_API void SetTooltip(const char* fmt, ...) IM_FMTARGS(1); // set a text-only tooltip, typically use with ImGui::IsItemHovered(). overidde any previous call to SetTooltip(). + IMGUI_API void SetTooltip(const char* fmt, ...) IM_FMTARGS(1); // set a text-only tooltip, typically use with ImGui::IsItemHovered(). override any previous call to SetTooltip(). IMGUI_API void SetTooltipV(const char* fmt, va_list args) IM_FMTLIST(1); - // Popups + // Popups, Modals + // The properties of popups windows are: + // - They block normal mouse hovering detection outside them. (*) + // - Unless modal, they can be closed by clicking anywhere outside them, or by pressing ESCAPE. + // - Their visibility state (~bool) is held internally by imgui instead of being held by the programmer as we are used to with regular Begin() calls. + // User can manipulate the visibility state by calling OpenPopup(). + // (*) One can use IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup) to bypass it and detect hovering even when normally blocked by a popup. + // Those three properties are connected. The library needs to hold their visibility state because it can close popups at any time. IMGUI_API void OpenPopup(const char* str_id); // call to mark popup as open (don't call every frame!). popups are closed when user click outside, or if CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block. By default, Selectable()/MenuItem() are calling CloseCurrentPopup(). Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level). IMGUI_API bool BeginPopup(const char* str_id, ImGuiWindowFlags flags = 0); // return true if the popup is open, and you can start outputting to it. only call EndPopup() if BeginPopup() returns true! IMGUI_API bool BeginPopupContextItem(const char* str_id = NULL, int mouse_button = 1); // helper to open and begin popup when clicked on last item. if you can pass a NULL str_id only if the previous item had an id. If you want to use that on a non-interactive item such as Text() you need to pass in an explicit ID here. read comments in .cpp! @@ -487,11 +555,12 @@ namespace ImGui IMGUI_API bool BeginPopupModal(const char* name, bool* p_open = NULL, ImGuiWindowFlags flags = 0); // modal dialog (regular window with title bar, block interactions behind the modal window, can't close the modal window by clicking outside) IMGUI_API void EndPopup(); // only call EndPopup() if BeginPopupXXX() returns true! IMGUI_API bool OpenPopupOnItemClick(const char* str_id = NULL, int mouse_button = 1); // helper to open popup when clicked on last item (note: actually triggers on the mouse _released_ event to be consistent with popup behaviors). return true when just opened. - IMGUI_API bool IsPopupOpen(const char* str_id); // return true if the popup is open + IMGUI_API bool IsPopupOpen(const char* str_id); // return true if the popup is open at the current begin-ed level of the popup stack. IMGUI_API void CloseCurrentPopup(); // close the popup we have begin-ed into. clicking on a MenuItem or Selectable automatically close the current popup. // Columns - // You can also use SameLine(pos_x) for simplified columns. The columns API is work-in-progress and rather lacking (columns are arguably the worst part of dear imgui at the moment!) + // - You can also use SameLine(pos_x) to mimic simplified columns. + // - The columns API is work-in-progress and rather lacking (columns are arguably the worst part of dear imgui at the moment!) IMGUI_API void Columns(int count = 1, const char* id = NULL, bool border = true); IMGUI_API void NextColumn(); // next column, defaults to current row or next row if the current row is finished IMGUI_API int GetColumnIndex(); // get current column index @@ -501,59 +570,71 @@ namespace ImGui IMGUI_API void SetColumnOffset(int column_index, float offset_x); // set position of column line (in pixels, from the left side of the contents region). pass -1 to use current column IMGUI_API int GetColumnsCount(); - // Logging/Capture: all text output from interface is captured to tty/file/clipboard. By default, tree nodes are automatically opened during logging. - IMGUI_API void LogToTTY(int max_depth = -1); // start logging to tty - IMGUI_API void LogToFile(int max_depth = -1, const char* filename = NULL); // start logging to file - IMGUI_API void LogToClipboard(int max_depth = -1); // start logging to OS clipboard + // Tab Bars, Tabs + // [BETA API] API may evolve! + IMGUI_API bool BeginTabBar(const char* str_id, ImGuiTabBarFlags flags = 0); // create and append into a TabBar + IMGUI_API void EndTabBar(); // only call EndTabBar() if BeginTabBar() returns true! + IMGUI_API bool BeginTabItem(const char* label, bool* p_open = NULL, ImGuiTabItemFlags flags = 0);// create a Tab. Returns true if the Tab is selected. + IMGUI_API void EndTabItem(); // only call EndTabItem() if BeginTabItem() returns true! + IMGUI_API void SetTabItemClosed(const char* tab_or_docked_window_label); // notify TabBar or Docking system of a closed tab/window ahead (useful to reduce visual flicker on reorderable tab bars). For tab-bar: call after BeginTabBar() and before Tab submissions. Otherwise call with a window name. + + // Logging/Capture + // - All text output from the interface can be captured into tty/file/clipboard. By default, tree nodes are automatically opened during logging. + IMGUI_API void LogToTTY(int auto_open_depth = -1); // start logging to tty (stdout) + IMGUI_API void LogToFile(int auto_open_depth = -1, const char* filename = NULL); // start logging to file + IMGUI_API void LogToClipboard(int auto_open_depth = -1); // start logging to OS clipboard IMGUI_API void LogFinish(); // stop logging (close file, etc.) IMGUI_API void LogButtons(); // helper to display buttons for logging to tty/file/clipboard IMGUI_API void LogText(const char* fmt, ...) IM_FMTARGS(1); // pass text data straight to log (without being displayed) // Drag and Drop - // [BETA API] Missing Demo code. API may evolve. + // [BETA API] API may evolve! IMGUI_API bool BeginDragDropSource(ImGuiDragDropFlags flags = 0); // call when the current item is active. If this return true, you can call SetDragDropPayload() + EndDragDropSource() - IMGUI_API bool SetDragDropPayload(const char* type, const void* data, size_t size, ImGuiCond cond = 0);// type is a user defined string of maximum 32 characters. Strings starting with '_' are reserved for dear imgui internal types. Data is copied and held by imgui. + IMGUI_API bool SetDragDropPayload(const char* type, const void* data, size_t sz, ImGuiCond cond = 0); // type is a user defined string of maximum 32 characters. Strings starting with '_' are reserved for dear imgui internal types. Data is copied and held by imgui. IMGUI_API void EndDragDropSource(); // only call EndDragDropSource() if BeginDragDropSource() returns true! IMGUI_API bool BeginDragDropTarget(); // call after submitting an item that may receive a payload. If this returns true, you can call AcceptDragDropPayload() + EndDragDropTarget() IMGUI_API const ImGuiPayload* AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags = 0); // accept contents of a given type. If ImGuiDragDropFlags_AcceptBeforeDelivery is set you can peek into the payload before the mouse button is released. IMGUI_API void EndDragDropTarget(); // only call EndDragDropTarget() if BeginDragDropTarget() returns true! IMGUI_API const ImGuiPayload* GetDragDropPayload(); // peek directly into the current payload from anywhere. may return NULL. use ImGuiPayload::IsDataType() to test for the payload type. - + // Clipping IMGUI_API void PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect); IMGUI_API void PopClipRect(); // Focus, Activation - // (Prefer using "SetItemDefaultFocus()" over "if (IsWindowAppearing()) SetScrollHereY()" when applicable to signify "this is the default item") + // - Prefer using "SetItemDefaultFocus()" over "if (IsWindowAppearing()) SetScrollHereY()" when applicable to signify "this is the default item" IMGUI_API void SetItemDefaultFocus(); // make last item the default focused item of a window. IMGUI_API void SetKeyboardFocusHere(int offset = 0); // focus keyboard on the next widget. Use positive 'offset' to access sub components of a multiple component widget. Use -1 to access previous widget. // Item/Widgets Utilities - // See Demo Window under "Widgets->Querying Status" for an interactive visualization of many of those functions. + // - Most of the functions are referring to the last/previous item we submitted. + // - See Demo Window under "Widgets->Querying Status" for an interactive visualization of most of those functions. IMGUI_API bool IsItemHovered(ImGuiHoveredFlags flags = 0); // is the last item hovered? (and usable, aka not blocked by a popup, etc.). See ImGuiHoveredFlags for more options. IMGUI_API bool IsItemActive(); // is the last item active? (e.g. button being held, text field being edited. This will continuously return true while holding mouse button on an item. Items that don't interact will always return false) IMGUI_API bool IsItemFocused(); // is the last item focused for keyboard/gamepad navigation? IMGUI_API bool IsItemClicked(int mouse_button = 0); // is the last item clicked? (e.g. button/node just clicked on) == IsMouseClicked(mouse_button) && IsItemHovered() IMGUI_API bool IsItemVisible(); // is the last item visible? (items may be out of sight because of clipping/scrolling) IMGUI_API bool IsItemEdited(); // did the last item modify its underlying value this frame? or was pressed? This is generally the same as the "bool" return value of many widgets. + IMGUI_API bool IsItemActivated(); // was the last item just made active (item was previously inactive). IMGUI_API bool IsItemDeactivated(); // was the last item just made inactive (item was previously active). Useful for Undo/Redo patterns with widgets that requires continuous editing. IMGUI_API bool IsItemDeactivatedAfterEdit(); // was the last item just made inactive and made a value change when it was active? (e.g. Slider/Drag moved). Useful for Undo/Redo patterns with widgets that requires continuous editing. Note that you may get false positives (some widgets such as Combo()/ListBox()/Selectable() will return true even when clicking an already selected item). - IMGUI_API bool IsAnyItemHovered(); - IMGUI_API bool IsAnyItemActive(); - IMGUI_API bool IsAnyItemFocused(); - IMGUI_API ImVec2 GetItemRectMin(); // get bounding rectangle of last item, in screen space - IMGUI_API ImVec2 GetItemRectMax(); // " - IMGUI_API ImVec2 GetItemRectSize(); // get size of last item, in screen space + IMGUI_API bool IsAnyItemHovered(); // is any item hovered? + IMGUI_API bool IsAnyItemActive(); // is any item active? + IMGUI_API bool IsAnyItemFocused(); // is any item focused? + IMGUI_API ImVec2 GetItemRectMin(); // get upper-left bounding rectangle of the last item (screen space) + IMGUI_API ImVec2 GetItemRectMax(); // get lower-right bounding rectangle of the last item (screen space) + IMGUI_API ImVec2 GetItemRectSize(); // get size of last item IMGUI_API void SetItemAllowOverlap(); // allow last item to be overlapped by a subsequent item. sometimes useful with invisible buttons, selectables, etc. to catch unused area. // Miscellaneous Utilities IMGUI_API bool IsRectVisible(const ImVec2& size); // test if rectangle (of given size, starting from cursor position) is visible / not clipped. IMGUI_API bool IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max); // test if rectangle (in screen space) is visible / not clipped. to perform coarse clipping on user's side. - IMGUI_API double GetTime(); - IMGUI_API int GetFrameCount(); - IMGUI_API ImDrawList* GetOverlayDrawList(); // this draw list will be the last rendered one, useful to quickly draw overlays shapes/text - IMGUI_API ImDrawListSharedData* GetDrawListSharedData(); // you may use this when creating your own ImDrawList instances - IMGUI_API const char* GetStyleColorName(ImGuiCol idx); + IMGUI_API double GetTime(); // get global imgui time. incremented by io.DeltaTime every frame. + IMGUI_API int GetFrameCount(); // get global imgui frame count. incremented by 1 every frame. + IMGUI_API ImDrawList* GetBackgroundDrawList(); // this draw list will be the first rendering one. Useful to quickly draw shapes/text behind dear imgui contents. + IMGUI_API ImDrawList* GetForegroundDrawList(); // this draw list will be the last rendered one. Useful to quickly draw shapes/text over dear imgui contents. + IMGUI_API ImDrawListSharedData* GetDrawListSharedData(); // you may use this when creating your own ImDrawList instances. + IMGUI_API const char* GetStyleColorName(ImGuiCol idx); // get a string corresponding to the enum value (for display, saving, etc.). IMGUI_API void SetStateStorage(ImGuiStorage* storage); // replace current window storage with our own (if you want to manipulate it yourself, typically clear subsection of it) IMGUI_API ImGuiStorage* GetStateStorage(); IMGUI_API ImVec2 CalcTextSize(const char* text, const char* text_end = NULL, bool hide_text_after_double_hash = false, float wrap_width = -1.0f); @@ -579,38 +660,42 @@ namespace ImGui IMGUI_API bool IsMouseDoubleClicked(int button); // did mouse button double-clicked. a double-click returns false in IsMouseClicked(). uses io.MouseDoubleClickTime. IMGUI_API bool IsMouseReleased(int button); // did mouse button released (went from Down to !Down) IMGUI_API bool IsMouseDragging(int button = 0, float lock_threshold = -1.0f); // is mouse dragging. if lock_threshold < -1.0f uses io.MouseDraggingThreshold - IMGUI_API bool IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip = true); // is mouse hovering given bounding rect (in screen space). clipped by current clipping settings, but disregarding of other consideration of focus/window ordering/popup-block. - IMGUI_API bool IsMousePosValid(const ImVec2* mouse_pos = NULL); // + IMGUI_API bool IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip = true); // is mouse hovering given bounding rect (in screen space). clipped by current clipping settings, but disregarding of other consideration of focus/window ordering/popup-block. + IMGUI_API bool IsMousePosValid(const ImVec2* mouse_pos = NULL); // by convention we use (-FLT_MAX,-FLT_MAX) to denote that there is no mouse IMGUI_API ImVec2 GetMousePos(); // shortcut to ImGui::GetIO().MousePos provided by user, to be consistent with other calls IMGUI_API ImVec2 GetMousePosOnOpeningCurrentPopup(); // retrieve backup of mouse position at the time of opening popup we have BeginPopup() into - IMGUI_API ImVec2 GetMouseDragDelta(int button = 0, float lock_threshold = -1.0f); // dragging amount since clicking. if lock_threshold < -1.0f uses io.MouseDraggingThreshold + IMGUI_API ImVec2 GetMouseDragDelta(int button = 0, float lock_threshold = -1.0f); // return the delta from the initial clicking position. This is locked and return 0.0f until the mouse moves past a distance threshold at least once. If lock_threshold < -1.0f uses io.MouseDraggingThreshold IMGUI_API void ResetMouseDragDelta(int button = 0); // IMGUI_API ImGuiMouseCursor GetMouseCursor(); // get desired cursor type, reset in ImGui::NewFrame(), this is updated during the frame. valid before Render(). If you use software rendering by setting io.MouseDrawCursor ImGui will render those for you IMGUI_API void SetMouseCursor(ImGuiMouseCursor type); // set desired cursor type - IMGUI_API void CaptureKeyboardFromApp(bool capture = true); // manually override io.WantCaptureKeyboard flag next frame (said flag is entirely left for your application to handle). e.g. force capture keyboard when your widget is being hovered. - IMGUI_API void CaptureMouseFromApp(bool capture = true); // manually override io.WantCaptureMouse flag next frame (said flag is entirely left for your application to handle). + IMGUI_API void CaptureKeyboardFromApp(bool want_capture_keyboard_value = true); // attention: misleading name! manually override io.WantCaptureKeyboard flag next frame (said flag is entirely left for your application to handle). e.g. force capture keyboard when your widget is being hovered. This is equivalent to setting "io.WantCaptureKeyboard = want_capture_keyboard_value"; after the next NewFrame() call. + IMGUI_API void CaptureMouseFromApp(bool want_capture_mouse_value = true); // attention: misleading name! manually override io.WantCaptureMouse flag next frame (said flag is entirely left for your application to handle). This is equivalent to setting "io.WantCaptureMouse = want_capture_mouse_value;" after the next NewFrame() call. // Clipboard Utilities (also see the LogToClipboard() function to capture or output text data to the clipboard) IMGUI_API const char* GetClipboardText(); IMGUI_API void SetClipboardText(const char* text); // Settings/.Ini Utilities - // The disk functions are automatically called if io.IniFilename != NULL (default is "imgui.ini"). - // Set io.IniFilename to NULL to load/save manually. Read io.WantSaveIniSettings description about handling .ini saving manually. + // - The disk functions are automatically called if io.IniFilename != NULL (default is "imgui.ini"). + // - Set io.IniFilename to NULL to load/save manually. Read io.WantSaveIniSettings description about handling .ini saving manually. IMGUI_API void LoadIniSettingsFromDisk(const char* ini_filename); // call after CreateContext() and before the first call to NewFrame(). NewFrame() automatically calls LoadIniSettingsFromDisk(io.IniFilename). IMGUI_API void LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size=0); // call after CreateContext() and before the first call to NewFrame() to provide .ini data from your own data source. - IMGUI_API void SaveIniSettingsToDisk(const char* ini_filename); + IMGUI_API void SaveIniSettingsToDisk(const char* ini_filename); // this is automatically called (if io.IniFilename is not empty) a few seconds after any modification that should be reflected in the .ini file (and also by DestroyContext). IMGUI_API const char* SaveIniSettingsToMemory(size_t* out_ini_size = NULL); // return a zero-terminated string with the .ini data which you can save by your own mean. call when io.WantSaveIniSettings is set, then save data by your own mean and clear io.WantSaveIniSettings. - // Memory Utilities - // All those functions are not reliant on the current context. - // If you reload the contents of imgui.cpp at runtime, you may need to call SetCurrentContext() + SetAllocatorFunctions() again. - IMGUI_API void SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void(*free_func)(void* ptr, void* user_data), void* user_data = NULL); + // Memory Allocators + // - All those functions are not reliant on the current context. + // - If you reload the contents of imgui.cpp at runtime, you may need to call SetCurrentContext() + SetAllocatorFunctions() again because we use global storage for those. + IMGUI_API void SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data = NULL); IMGUI_API void* MemAlloc(size_t size); IMGUI_API void MemFree(void* ptr); } // namespace ImGui +//----------------------------------------------------------------------------- +// Flags & Enumerations +//----------------------------------------------------------------------------- + // Flags for ImGui::Begin() enum ImGuiWindowFlags_ { @@ -618,7 +703,7 @@ enum ImGuiWindowFlags_ ImGuiWindowFlags_NoTitleBar = 1 << 0, // Disable title-bar ImGuiWindowFlags_NoResize = 1 << 1, // Disable user resizing with the lower-right grip ImGuiWindowFlags_NoMove = 1 << 2, // Disable user moving the window - ImGuiWindowFlags_NoScrollbar = 1 << 3, // Disable scrollbars (window can still scroll with mouse or programatically) + ImGuiWindowFlags_NoScrollbar = 1 << 3, // Disable scrollbars (window can still scroll with mouse or programmatically) ImGuiWindowFlags_NoScrollWithMouse = 1 << 4, // Disable user vertically scrolling with mouse wheel. On child window, mouse wheel will be forwarded to the parent unless NoScrollbar is also set. ImGuiWindowFlags_NoCollapse = 1 << 5, // Disable user collapsing window by double-clicking on it ImGuiWindowFlags_AlwaysAutoResize = 1 << 6, // Resize every window to its content every frame @@ -628,12 +713,13 @@ enum ImGuiWindowFlags_ ImGuiWindowFlags_MenuBar = 1 << 10, // Has a menu-bar ImGuiWindowFlags_HorizontalScrollbar = 1 << 11, // Allow horizontal scrollbar to appear (off by default). You may use SetNextWindowContentSize(ImVec2(width,0.0f)); prior to calling Begin() to specify width. Read code in imgui_demo in the "Horizontal Scrolling" section. ImGuiWindowFlags_NoFocusOnAppearing = 1 << 12, // Disable taking focus when transitioning from hidden to visible state - ImGuiWindowFlags_NoBringToFrontOnFocus = 1 << 13, // Disable bringing window to front when taking focus (e.g. clicking on it or programatically giving it focus) + ImGuiWindowFlags_NoBringToFrontOnFocus = 1 << 13, // Disable bringing window to front when taking focus (e.g. clicking on it or programmatically giving it focus) ImGuiWindowFlags_AlwaysVerticalScrollbar= 1 << 14, // Always show vertical scrollbar (even if ContentSize.y < Size.y) ImGuiWindowFlags_AlwaysHorizontalScrollbar=1<< 15, // Always show horizontal scrollbar (even if ContentSize.x < Size.x) ImGuiWindowFlags_AlwaysUseWindowPadding = 1 << 16, // Ensure child windows without border uses style.WindowPadding (ignored by default for non-bordered child windows, because more convenient) ImGuiWindowFlags_NoNavInputs = 1 << 18, // No gamepad/keyboard navigation within the window ImGuiWindowFlags_NoNavFocus = 1 << 19, // No focusing toward this window with gamepad/keyboard navigation (e.g. skipped by CTRL+TAB) + ImGuiWindowFlags_UnsavedDocument = 1 << 20, // Append '*' to title without affecting the ID, as a convenience to avoid using the ### operator. When used in a tab/docking context, tab is selected on closure and closure is deferred by one frame to allow code to cancel the closure (with a confirmation popup, etc.) without flicker. ImGuiWindowFlags_NoNav = ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus, ImGuiWindowFlags_NoDecoration = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse, ImGuiWindowFlags_NoInputs = ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus, @@ -648,7 +734,7 @@ enum ImGuiWindowFlags_ // [Obsolete] //ImGuiWindowFlags_ShowBorders = 1 << 7, // --> Set style.FrameBorderSize=1.0f / style.WindowBorderSize=1.0f to enable borders around windows and items - //ImGuiWindowFlags_ResizeFromAnySide = 1 << 17, // --> Set io.ConfigResizeWindowsFromEdges and make sure mouse cursors are supported by back-end (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) + //ImGuiWindowFlags_ResizeFromAnySide = 1 << 17, // --> Set io.ConfigWindowsResizeFromEdges and make sure mouse cursors are supported by back-end (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) }; // Flags for ImGui::InputText() @@ -660,7 +746,7 @@ enum ImGuiInputTextFlags_ ImGuiInputTextFlags_CharsUppercase = 1 << 2, // Turn a..z into A..Z ImGuiInputTextFlags_CharsNoBlank = 1 << 3, // Filter out spaces, tabs ImGuiInputTextFlags_AutoSelectAll = 1 << 4, // Select entire text when first taking mouse focus - ImGuiInputTextFlags_EnterReturnsTrue = 1 << 5, // Return 'true' when Enter is pressed (as opposed to when the value was modified) + ImGuiInputTextFlags_EnterReturnsTrue = 1 << 5, // Return 'true' when Enter is pressed (as opposed to every time the value was modified). Consider looking at the IsItemDeactivatedAfterEdit() function. ImGuiInputTextFlags_CallbackCompletion = 1 << 6, // Callback on pressing TAB (for completion handling) ImGuiInputTextFlags_CallbackHistory = 1 << 7, // Callback on pressing Up/Down arrows (for history handling) ImGuiInputTextFlags_CallbackAlways = 1 << 8, // Callback on each iteration. User code may query cursor position, modify text buffer. @@ -728,6 +814,32 @@ enum ImGuiComboFlags_ ImGuiComboFlags_HeightMask_ = ImGuiComboFlags_HeightSmall | ImGuiComboFlags_HeightRegular | ImGuiComboFlags_HeightLarge | ImGuiComboFlags_HeightLargest }; +// Flags for ImGui::BeginTabBar() +enum ImGuiTabBarFlags_ +{ + ImGuiTabBarFlags_None = 0, + ImGuiTabBarFlags_Reorderable = 1 << 0, // Allow manually dragging tabs to re-order them + New tabs are appended at the end of list + ImGuiTabBarFlags_AutoSelectNewTabs = 1 << 1, // Automatically select new tabs when they appear + ImGuiTabBarFlags_TabListPopupButton = 1 << 2, + ImGuiTabBarFlags_NoCloseWithMiddleMouseButton = 1 << 3, // Disable behavior of closing tabs (that are submitted with p_open != NULL) with middle mouse button. You can still repro this behavior on user's side with if (IsItemHovered() && IsMouseClicked(2)) *p_open = false. + ImGuiTabBarFlags_NoTabListScrollingButtons = 1 << 4, + ImGuiTabBarFlags_NoTooltip = 1 << 5, // Disable tooltips when hovering a tab + ImGuiTabBarFlags_FittingPolicyResizeDown = 1 << 6, // Resize tabs when they don't fit + ImGuiTabBarFlags_FittingPolicyScroll = 1 << 7, // Add scroll buttons when tabs don't fit + ImGuiTabBarFlags_FittingPolicyMask_ = ImGuiTabBarFlags_FittingPolicyResizeDown | ImGuiTabBarFlags_FittingPolicyScroll, + ImGuiTabBarFlags_FittingPolicyDefault_ = ImGuiTabBarFlags_FittingPolicyResizeDown +}; + +// Flags for ImGui::BeginTabItem() +enum ImGuiTabItemFlags_ +{ + ImGuiTabItemFlags_None = 0, + ImGuiTabItemFlags_UnsavedDocument = 1 << 0, // Append '*' to title without affecting the ID, as a convenience to avoid using the ### operator. Also: tab is selected on closure and closure is deferred by one frame to allow code to undo it without flicker. + ImGuiTabItemFlags_SetSelected = 1 << 1, // Trigger flag to programmatically make the tab selected when calling BeginTabItem() + ImGuiTabItemFlags_NoCloseWithMiddleMouseButton = 1 << 2, // Disable behavior of closing tabs (that are submitted with p_open != NULL) with middle mouse button. You can still repro this behavior on user's side with if (IsItemHovered() && IsMouseClicked(2)) *p_open = false. + ImGuiTabItemFlags_NoPushId = 1 << 3 // Don't call PushID(tab->ID)/PopID() on BeginTabItem()/EndTabItem() +}; + // Flags for ImGui::IsWindowFocused() enum ImGuiFocusedFlags_ { @@ -781,10 +893,14 @@ enum ImGuiDragDropFlags_ // A primary data type enum ImGuiDataType_ { + ImGuiDataType_S8, // char + ImGuiDataType_U8, // unsigned char + ImGuiDataType_S16, // short + ImGuiDataType_U16, // unsigned short ImGuiDataType_S32, // int ImGuiDataType_U32, // unsigned int - ImGuiDataType_S64, // long long, __int64 - ImGuiDataType_U64, // unsigned long long, unsigned __int64 + ImGuiDataType_S64, // long long / __int64 + ImGuiDataType_U64, // unsigned long long / unsigned __int64 ImGuiDataType_Float, // float ImGuiDataType_Double, // double ImGuiDataType_COUNT @@ -855,6 +971,7 @@ enum ImGuiNavInput_ // [Internal] Don't use directly! This is used internally to differentiate keyboard from gamepad inputs for behaviors that require to differentiate them. // Keyboard behavior that have no corresponding gamepad mapping (e.g. CTRL+TAB) will be directly reading from io.KeysDown[] instead of io.NavInputs[]. ImGuiNavInput_KeyMenu_, // toggle menu // = io.KeyAlt + ImGuiNavInput_KeyTab_, // tab // = Tab key ImGuiNavInput_KeyLeft_, // move left // = Arrow keys ImGuiNavInput_KeyRight_, // move right ImGuiNavInput_KeyUp_, // move up @@ -866,6 +983,7 @@ enum ImGuiNavInput_ // Configuration flags stored in io.ConfigFlags. Set by user/application. enum ImGuiConfigFlags_ { + ImGuiConfigFlags_None = 0, ImGuiConfigFlags_NavEnableKeyboard = 1 << 0, // Master keyboard navigation enable flag. NewFrame() will automatically fill io.NavInputs[] based on io.KeysDown[]. ImGuiConfigFlags_NavEnableGamepad = 1 << 1, // Master gamepad navigation enable flag. This is mostly to instruct your imgui back-end to fill io.NavInputs[]. Back-end also needs to set ImGuiBackendFlags_HasGamepad. ImGuiConfigFlags_NavEnableSetMousePos = 1 << 2, // Instruct navigation to move the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is awkward. Will update io.MousePos and set io.WantSetMousePos=true. If enabled you MUST honor io.WantSetMousePos requests in your binding, otherwise ImGui will react as if the mouse is jumping around back and forth. @@ -881,6 +999,7 @@ enum ImGuiConfigFlags_ // Back-end capabilities flags stored in io.BackendFlags. Set by imgui_impl_xxx or custom back-end. enum ImGuiBackendFlags_ { + ImGuiBackendFlags_None = 0, ImGuiBackendFlags_HasGamepad = 1 << 0, // Back-end supports gamepad and currently has one connected. ImGuiBackendFlags_HasMouseCursors = 1 << 1, // Back-end supports honoring GetMouseCursor() value to change the OS cursor shape. ImGuiBackendFlags_HasSetMousePos = 1 << 2 // Back-end supports io.WantSetMousePos requests to reposition the OS mouse position (only used if ImGuiConfigFlags_NavEnableSetMousePos is set). @@ -922,6 +1041,11 @@ enum ImGuiCol_ ImGuiCol_ResizeGrip, ImGuiCol_ResizeGripHovered, ImGuiCol_ResizeGripActive, + ImGuiCol_Tab, + ImGuiCol_TabHovered, + ImGuiCol_TabActive, + ImGuiCol_TabUnfocused, + ImGuiCol_TabUnfocusedActive, ImGuiCol_PlotLines, ImGuiCol_PlotLinesHovered, ImGuiCol_PlotHistogram, @@ -936,8 +1060,9 @@ enum ImGuiCol_ // Obsolete names (will be removed) #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - , ImGuiCol_ChildWindowBg = ImGuiCol_ChildBg, ImGuiCol_Column = ImGuiCol_Separator, ImGuiCol_ColumnHovered = ImGuiCol_SeparatorHovered, ImGuiCol_ColumnActive = ImGuiCol_SeparatorActive - , ImGuiCol_ModalWindowDarkening = ImGuiCol_ModalWindowDimBg + , ImGuiCol_ModalWindowDarkening = ImGuiCol_ModalWindowDimBg // [renamed in 1.63] + , ImGuiCol_ChildWindowBg = ImGuiCol_ChildBg // [renamed in 1.53] + , ImGuiCol_Column = ImGuiCol_Separator, ImGuiCol_ColumnHovered = ImGuiCol_SeparatorHovered, ImGuiCol_ColumnActive = ImGuiCol_SeparatorActive // [renamed in 1.51] //ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered, // [unused since 1.60+] the close button now uses regular button colors. //ImGuiCol_ComboBg, // [unused since 1.53+] ComboBg has been merged with PopupBg, so a redirect isn't accurate. #endif @@ -948,7 +1073,7 @@ enum ImGuiCol_ // NB: if changing this enum, you need to update the associated internal table GStyleVarInfo[] accordingly. This is where we link enum values to members offset/type. enum ImGuiStyleVar_ { - // Enum name ......................// Member in ImGuiStyle structure (see ImGuiStyle for descriptions) + // Enum name --------------------- // Member in ImGuiStyle structure (see ImGuiStyle for descriptions) ImGuiStyleVar_Alpha, // float Alpha ImGuiStyleVar_WindowPadding, // ImVec2 WindowPadding ImGuiStyleVar_WindowRounding, // float WindowRounding @@ -969,7 +1094,9 @@ enum ImGuiStyleVar_ ImGuiStyleVar_ScrollbarRounding, // float ScrollbarRounding ImGuiStyleVar_GrabMinSize, // float GrabMinSize ImGuiStyleVar_GrabRounding, // float GrabRounding + ImGuiStyleVar_TabRounding, // float TabRounding ImGuiStyleVar_ButtonTextAlign, // ImVec2 ButtonTextAlign + ImGuiStyleVar_SelectableTextAlign, // ImVec2 SelectableTextAlign ImGuiStyleVar_COUNT // Obsolete names (will be removed) @@ -978,11 +1105,11 @@ enum ImGuiStyleVar_ #endif }; -// Enumeration for ColorEdit3() / ColorEdit4() / ColorPicker3() / ColorPicker4() / ColorButton() +// Flags for ColorEdit3() / ColorEdit4() / ColorPicker3() / ColorPicker4() / ColorButton() enum ImGuiColorEditFlags_ { ImGuiColorEditFlags_None = 0, - ImGuiColorEditFlags_NoAlpha = 1 << 1, // // ColorEdit, ColorPicker, ColorButton: ignore Alpha component (read 3 components from the input pointer). + ImGuiColorEditFlags_NoAlpha = 1 << 1, // // ColorEdit, ColorPicker, ColorButton: ignore Alpha component (will only read 3 components from the input pointer). ImGuiColorEditFlags_NoPicker = 1 << 2, // // ColorEdit: disable picker when clicking on colored square. ImGuiColorEditFlags_NoOptions = 1 << 3, // // ColorEdit: disable toggling options menu when right-clicking on inputs/small preview. ImGuiColorEditFlags_NoSmallPreview = 1 << 4, // // ColorEdit, ColorPicker: disable colored square preview next to the inputs. (e.g. to show only the inputs) @@ -992,24 +1119,35 @@ enum ImGuiColorEditFlags_ ImGuiColorEditFlags_NoSidePreview = 1 << 8, // // ColorPicker: disable bigger color preview on right side of the picker, use small colored square preview instead. ImGuiColorEditFlags_NoDragDrop = 1 << 9, // // ColorEdit: disable drag and drop target. ColorButton: disable drag and drop source. - // User Options (right-click on widget to change some of them). You can set application defaults using SetColorEditOptions(). The idea is that you probably don't want to override them in most of your calls, let the user choose and/or call SetColorEditOptions() during startup. + // User Options (right-click on widget to change some of them). ImGuiColorEditFlags_AlphaBar = 1 << 16, // // ColorEdit, ColorPicker: show vertical alpha bar/gradient in picker. ImGuiColorEditFlags_AlphaPreview = 1 << 17, // // ColorEdit, ColorPicker, ColorButton: display preview as a transparent color over a checkerboard, instead of opaque. ImGuiColorEditFlags_AlphaPreviewHalf= 1 << 18, // // ColorEdit, ColorPicker, ColorButton: display half opaque / half checkerboard, instead of opaque. ImGuiColorEditFlags_HDR = 1 << 19, // // (WIP) ColorEdit: Currently only disable 0.0f..1.0f limits in RGBA edition (note: you probably want to use ImGuiColorEditFlags_Float flag as well). - ImGuiColorEditFlags_RGB = 1 << 20, // [Inputs] // ColorEdit: choose one among RGB/HSV/HEX. ColorPicker: choose any combination using RGB/HSV/HEX. - ImGuiColorEditFlags_HSV = 1 << 21, // [Inputs] // " - ImGuiColorEditFlags_HEX = 1 << 22, // [Inputs] // " + ImGuiColorEditFlags_DisplayRGB = 1 << 20, // [Display] // ColorEdit: override _display_ type among RGB/HSV/Hex. ColorPicker: select any combination using one or more of RGB/HSV/Hex. + ImGuiColorEditFlags_DisplayHSV = 1 << 21, // [Display] // " + ImGuiColorEditFlags_DisplayHex = 1 << 22, // [Display] // " ImGuiColorEditFlags_Uint8 = 1 << 23, // [DataType] // ColorEdit, ColorPicker, ColorButton: _display_ values formatted as 0..255. ImGuiColorEditFlags_Float = 1 << 24, // [DataType] // ColorEdit, ColorPicker, ColorButton: _display_ values formatted as 0.0f..1.0f floats instead of 0..255 integers. No round-trip of value via integers. - ImGuiColorEditFlags_PickerHueBar = 1 << 25, // [PickerMode] // ColorPicker: bar for Hue, rectangle for Sat/Value. - ImGuiColorEditFlags_PickerHueWheel = 1 << 26, // [PickerMode] // ColorPicker: wheel for Hue, triangle for Sat/Value. + ImGuiColorEditFlags_PickerHueBar = 1 << 25, // [Picker] // ColorPicker: bar for Hue, rectangle for Sat/Value. + ImGuiColorEditFlags_PickerHueWheel = 1 << 26, // [Picker] // ColorPicker: wheel for Hue, triangle for Sat/Value. + ImGuiColorEditFlags_InputRGB = 1 << 27, // [Input] // ColorEdit, ColorPicker: input and output data in RGB format. + ImGuiColorEditFlags_InputHSV = 1 << 28, // [Input] // ColorEdit, ColorPicker: input and output data in HSV format. + + // Defaults Options. You can set application defaults using SetColorEditOptions(). The intent is that you probably don't want to + // override them in most of your calls. Let the user choose via the option menu and/or call SetColorEditOptions() once during startup. + ImGuiColorEditFlags__OptionsDefault = ImGuiColorEditFlags_Uint8|ImGuiColorEditFlags_DisplayRGB|ImGuiColorEditFlags_InputRGB|ImGuiColorEditFlags_PickerHueBar, // [Internal] Masks - ImGuiColorEditFlags__InputsMask = ImGuiColorEditFlags_RGB|ImGuiColorEditFlags_HSV|ImGuiColorEditFlags_HEX, + ImGuiColorEditFlags__DisplayMask = ImGuiColorEditFlags_DisplayRGB|ImGuiColorEditFlags_DisplayHSV|ImGuiColorEditFlags_DisplayHex, ImGuiColorEditFlags__DataTypeMask = ImGuiColorEditFlags_Uint8|ImGuiColorEditFlags_Float, ImGuiColorEditFlags__PickerMask = ImGuiColorEditFlags_PickerHueWheel|ImGuiColorEditFlags_PickerHueBar, - ImGuiColorEditFlags__OptionsDefault = ImGuiColorEditFlags_Uint8|ImGuiColorEditFlags_RGB|ImGuiColorEditFlags_PickerHueBar // Change application default using SetColorEditOptions() + ImGuiColorEditFlags__InputMask = ImGuiColorEditFlags_InputRGB|ImGuiColorEditFlags_InputHSV + + // Obsolete names (will be removed) +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + , ImGuiColorEditFlags_RGB = ImGuiColorEditFlags_DisplayRGB, ImGuiColorEditFlags_HSV = ImGuiColorEditFlags_DisplayHSV, ImGuiColorEditFlags_HEX = ImGuiColorEditFlags_DisplayHex +#endif }; // Enumeration for GetMouseCursor() @@ -1033,7 +1171,8 @@ enum ImGuiMouseCursor_ #endif }; -// Condition for ImGui::SetWindow***(), SetNextWindow***(), SetNextTreeNode***() functions +// Enumateration for ImGui::SetWindow***(), SetNextWindow***(), SetNextTreeNode***() functions +// Represent a condition. // Important: Treat as a regular enum! Do NOT combine multiple values using binary operators! All the functions above treat 0 as a shortcut to ImGuiCond_Always. enum ImGuiCond_ { @@ -1048,8 +1187,75 @@ enum ImGuiCond_ #endif }; +//----------------------------------------------------------------------------- +// Helper: ImVector<> +// Lightweight std::vector<>-like class to avoid dragging dependencies (also, some implementations of STL with debug enabled are absurdly slow, we bypass it so our code runs fast in debug). +// You generally do NOT need to care or use this ever. But we need to make it available in imgui.h because some of our data structures are relying on it. +// Important: clear() frees memory, resize(0) keep the allocated buffer. We use resize(0) a lot to intentionally recycle allocated buffers across frames and amortize our costs. +// Important: our implementation does NOT call C++ constructors/destructors, we treat everything as raw data! This is intentional but be extra mindful of that, +// do NOT use this class as a std::vector replacement in your own code! Many of the structures used by dear imgui can be safely initialized by a zero-memset. +//----------------------------------------------------------------------------- + +template +struct ImVector +{ + int Size; + int Capacity; + T* Data; + + // Provide standard typedefs but we don't use them ourselves. + typedef T value_type; + typedef value_type* iterator; + typedef const value_type* const_iterator; + + // Constructors, destructor + inline ImVector() { Size = Capacity = 0; Data = NULL; } + inline ImVector(const ImVector& src) { Size = Capacity = 0; Data = NULL; operator=(src); } + inline ImVector& operator=(const ImVector& src) { clear(); resize(src.Size); memcpy(Data, src.Data, (size_t)Size * sizeof(T)); return *this; } + inline ~ImVector() { if (Data) ImGui::MemFree(Data); } + + inline bool empty() const { return Size == 0; } + inline int size() const { return Size; } + inline int size_in_bytes() const { return Size * (int)sizeof(T); } + inline int capacity() const { return Capacity; } + inline T& operator[](int i) { IM_ASSERT(i < Size); return Data[i]; } + inline const T& operator[](int i) const { IM_ASSERT(i < Size); return Data[i]; } + + inline void clear() { if (Data) { Size = Capacity = 0; ImGui::MemFree(Data); Data = NULL; } } + inline T* begin() { return Data; } + inline const T* begin() const { return Data; } + inline T* end() { return Data + Size; } + inline const T* end() const { return Data + Size; } + inline T& front() { IM_ASSERT(Size > 0); return Data[0]; } + inline const T& front() const { IM_ASSERT(Size > 0); return Data[0]; } + inline T& back() { IM_ASSERT(Size > 0); return Data[Size - 1]; } + inline const T& back() const { IM_ASSERT(Size > 0); return Data[Size - 1]; } + inline void swap(ImVector& rhs) { int rhs_size = rhs.Size; rhs.Size = Size; Size = rhs_size; int rhs_cap = rhs.Capacity; rhs.Capacity = Capacity; Capacity = rhs_cap; T* rhs_data = rhs.Data; rhs.Data = Data; Data = rhs_data; } + + inline int _grow_capacity(int sz) const { int new_capacity = Capacity ? (Capacity + Capacity/2) : 8; return new_capacity > sz ? new_capacity : sz; } + inline void resize(int new_size) { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; } + inline void resize(int new_size, const T& v) { if (new_size > Capacity) reserve(_grow_capacity(new_size)); if (new_size > Size) for (int n = Size; n < new_size; n++) memcpy(&Data[n], &v, sizeof(v)); Size = new_size; } + inline void reserve(int new_capacity) { if (new_capacity <= Capacity) return; T* new_data = (T*)ImGui::MemAlloc((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); ImGui::MemFree(Data); } Data = new_data; Capacity = new_capacity; } + + // NB: It is illegal to call push_back/push_front/insert with a reference pointing inside the ImVector data itself! e.g. v.push_back(v[10]) is forbidden. + inline void push_back(const T& v) { if (Size == Capacity) reserve(_grow_capacity(Size + 1)); memcpy(&Data[Size], &v, sizeof(v)); Size++; } + inline void pop_back() { IM_ASSERT(Size > 0); Size--; } + inline void push_front(const T& v) { if (Size == 0) push_back(v); else insert(Data, v); } + inline T* erase(const T* it) { IM_ASSERT(it >= Data && it < Data+Size); const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + 1, ((size_t)Size - (size_t)off - 1) * sizeof(T)); Size--; return Data + off; } + inline T* erase(const T* it, const T* it_last){ IM_ASSERT(it >= Data && it < Data+Size && it_last > it && it_last <= Data+Size); const ptrdiff_t count = it_last - it; const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + count, ((size_t)Size - (size_t)off - count) * sizeof(T)); Size -= (int)count; return Data + off; } + inline T* erase_unsorted(const T* it) { IM_ASSERT(it >= Data && it < Data+Size); const ptrdiff_t off = it - Data; if (it < Data+Size-1) memcpy(Data + off, Data + Size - 1, sizeof(T)); Size--; return Data + off; } + inline T* insert(const T* it, const T& v) { IM_ASSERT(it >= Data && it <= Data+Size); const ptrdiff_t off = it - Data; if (Size == Capacity) reserve(_grow_capacity(Size + 1)); if (off < (int)Size) memmove(Data + off + 1, Data + off, ((size_t)Size - (size_t)off) * sizeof(T)); memcpy(&Data[off], &v, sizeof(v)); Size++; return Data + off; } + inline bool contains(const T& v) const { const T* data = Data; const T* data_end = Data + Size; while (data < data_end) if (*data++ == v) return true; return false; } + inline int index_from_ptr(const T* it) const { IM_ASSERT(it >= Data && it <= Data+Size); const ptrdiff_t off = it - Data; return (int)off; } +}; + +//----------------------------------------------------------------------------- +// ImGuiStyle // You may modify the ImGui::GetStyle() main instance during initialization and before NewFrame(). -// During the frame, use ImGui::PushStyleVar(ImGuiStyleVar_XXXX)/PopStyleVar() to alter the main style values, and ImGui::PushStyleColor(ImGuiCol_XXX)/PopStyleColor() for colors. +// During the frame, use ImGui::PushStyleVar(ImGuiStyleVar_XXXX)/PopStyleVar() to alter the main style values, +// and ImGui::PushStyleColor(ImGuiCol_XXX)/PopStyleColor() for colors. +//----------------------------------------------------------------------------- + struct ImGuiStyle { float Alpha; // Global alpha applies to everything in ImGui. @@ -1074,7 +1280,10 @@ struct ImGuiStyle float ScrollbarRounding; // Radius of grab corners for scrollbar. float GrabMinSize; // Minimum width/height of a grab box for slider/scrollbar. float GrabRounding; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs. - ImVec2 ButtonTextAlign; // Alignment of button text when button is larger than text. Defaults to (0.5f,0.5f) for horizontally+vertically centered. + float TabRounding; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs. + float TabBorderSize; // Thickness of border around tabs. + ImVec2 ButtonTextAlign; // Alignment of button text when button is larger than text. Defaults to (0.5f, 0.5f) (centered). + ImVec2 SelectableTextAlign; // Alignment of selectable text when selectable is larger than text. Defaults to (0.0f, 0.0f) (top-left aligned). ImVec2 DisplayWindowPadding; // Window position are clamped to be visible within the display area by at least this amount. Only applies to regular windows. ImVec2 DisplaySafeAreaPadding; // If you cannot see the edges of your screen (e.g. on a TV) increase the safe area padding. Apply to popups/tooltips as well regular windows. NB: Prefer configuring your TV sets correctly! float MouseCursorScale; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later. @@ -1087,64 +1296,75 @@ struct ImGuiStyle IMGUI_API void ScaleAllSizes(float scale_factor); }; -// This is where your app communicate with Dear ImGui. Access via ImGui::GetIO(). -// Read 'Programmer guide' section in .cpp file for general usage. +//----------------------------------------------------------------------------- +// ImGuiIO +// Communicate most settings and inputs/outputs to Dear ImGui using this structure. +// Access via ImGui::GetIO(). Read 'Programmer guide' section in .cpp file for general usage. +//----------------------------------------------------------------------------- + struct ImGuiIO { //------------------------------------------------------------------ - // Configuration (fill once) // Default value: + // Configuration (fill once) // Default value //------------------------------------------------------------------ - ImGuiConfigFlags ConfigFlags; // = 0 // See ImGuiConfigFlags_ enum. Set by user/application. Gamepad/keyboard navigation options, etc. - ImGuiBackendFlags BackendFlags; // = 0 // Set ImGuiBackendFlags_ enum. Set by imgui_impl_xxx files or custom back-end to communicate features supported by the back-end. - ImVec2 DisplaySize; // // Main display size, in pixels. For clamping windows positions. - float DeltaTime; // = 1.0f/60.0f // Time elapsed since last frame, in seconds. - float IniSavingRate; // = 5.0f // Minimum time between saving positions/sizes to .ini file, in seconds. - const char* IniFilename; // = "imgui.ini" // Path to .ini file. Set NULL to disable automatic .ini loading/saving, if e.g. you want to manually load/save from memory. - const char* LogFilename; // = "imgui_log.txt" // Path to .log file (default parameter to ImGui::LogToFile when no file is specified). - float MouseDoubleClickTime; // = 0.30f // Time for a double-click, in seconds. - float MouseDoubleClickMaxDist; // = 6.0f // Distance threshold to stay in to validate a double-click, in pixels. - float MouseDragThreshold; // = 6.0f // Distance threshold before considering we are dragging. - int KeyMap[ImGuiKey_COUNT]; // // Map of indices into the KeysDown[512] entries array which represent your "native" keyboard state. - float KeyRepeatDelay; // = 0.250f // When holding a key/button, time before it starts repeating, in seconds (for buttons in Repeat mode, etc.). - float KeyRepeatRate; // = 0.050f // When holding a key/button, rate at which it repeats, in seconds. - void* UserData; // = NULL // Store your own data for retrieval by callbacks. - - ImFontAtlas* Fonts; // // Load and assemble one or more fonts into a single tightly packed texture. Output to Fonts array. - float FontGlobalScale; // = 1.0f // Global scale all fonts - bool FontAllowUserScaling; // = false // Allow user scaling text of individual window with CTRL+Wheel. - ImFont* FontDefault; // = NULL // Font to use on NewFrame(). Use NULL to uses Fonts->Fonts[0]. - ImVec2 DisplayFramebufferScale; // = (1.0f,1.0f) // For retina display or other situations where window coordinates are different from framebuffer coordinates. User storage only, presently not used by ImGui. - ImVec2 DisplayVisibleMin; // (0.0f,0.0f) // [obsolete] If you use DisplaySize as a virtual space larger than your screen, set DisplayVisibleMin/Max to the visible area. - ImVec2 DisplayVisibleMax; // (0.0f,0.0f) // [obsolete: just use io.DisplaySize] If the values are the same, we defaults to Min=(0.0f) and Max=DisplaySize - - // Miscellaneous configuration options - bool MouseDrawCursor; // = false // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). Cannot be easily renamed to 'io.ConfigXXX' because this is frequently used by back-end implementations. - bool ConfigMacOSXBehaviors; // = defined(__APPLE__) // OS X style: Text editing cursor movement using Alt instead of Ctrl, Shortcuts using Cmd/Super instead of Ctrl, Line/Text Start and End using Cmd+Arrows instead of Home/End, Double click selects by word instead of selecting whole text, Multi-selection in lists uses Cmd/Super instead of Ctrl (was called io.OptMacOSXBehaviors prior to 1.63) - bool ConfigInputTextCursorBlink; // = true // Set to false to disable blinking cursor, for users who consider it distracting. (was called: io.OptCursorBlink prior to 1.63) - bool ConfigResizeWindowsFromEdges; // = false // [BETA] Enable resizing of windows from their edges and from the lower-left corner. This requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback. (This used to be the ImGuiWindowFlags_ResizeFromAnySide flag) + ImGuiConfigFlags ConfigFlags; // = 0 // See ImGuiConfigFlags_ enum. Set by user/application. Gamepad/keyboard navigation options, etc. + ImGuiBackendFlags BackendFlags; // = 0 // See ImGuiBackendFlags_ enum. Set by back-end (imgui_impl_xxx files or custom back-end) to communicate features supported by the back-end. + ImVec2 DisplaySize; // // Main display size, in pixels. + float DeltaTime; // = 1.0f/60.0f // Time elapsed since last frame, in seconds. + float IniSavingRate; // = 5.0f // Minimum time between saving positions/sizes to .ini file, in seconds. + const char* IniFilename; // = "imgui.ini" // Path to .ini file. Set NULL to disable automatic .ini loading/saving, if e.g. you want to manually load/save from memory. + const char* LogFilename; // = "imgui_log.txt"// Path to .log file (default parameter to ImGui::LogToFile when no file is specified). + float MouseDoubleClickTime; // = 0.30f // Time for a double-click, in seconds. + float MouseDoubleClickMaxDist; // = 6.0f // Distance threshold to stay in to validate a double-click, in pixels. + float MouseDragThreshold; // = 6.0f // Distance threshold before considering we are dragging. + int KeyMap[ImGuiKey_COUNT]; // // Map of indices into the KeysDown[512] entries array which represent your "native" keyboard state. + float KeyRepeatDelay; // = 0.250f // When holding a key/button, time before it starts repeating, in seconds (for buttons in Repeat mode, etc.). + float KeyRepeatRate; // = 0.050f // When holding a key/button, rate at which it repeats, in seconds. + void* UserData; // = NULL // Store your own data for retrieval by callbacks. + + ImFontAtlas*Fonts; // // Load, rasterize and pack one or more fonts into a single texture. + float FontGlobalScale; // = 1.0f // Global scale all fonts + bool FontAllowUserScaling; // = false // Allow user scaling text of individual window with CTRL+Wheel. + ImFont* FontDefault; // = NULL // Font to use on NewFrame(). Use NULL to uses Fonts->Fonts[0]. + ImVec2 DisplayFramebufferScale; // = (1, 1) // For retina display or other situations where window coordinates are different from framebuffer coordinates. This generally ends up in ImDrawData::FramebufferScale. + + // Miscellaneous options + bool MouseDrawCursor; // = false // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). Cannot be easily renamed to 'io.ConfigXXX' because this is frequently used by back-end implementations. + bool ConfigMacOSXBehaviors; // = defined(__APPLE__) // OS X style: Text editing cursor movement using Alt instead of Ctrl, Shortcuts using Cmd/Super instead of Ctrl, Line/Text Start and End using Cmd+Arrows instead of Home/End, Double click selects by word instead of selecting whole text, Multi-selection in lists uses Cmd/Super instead of Ctrl (was called io.OptMacOSXBehaviors prior to 1.63) + bool ConfigInputTextCursorBlink; // = true // Set to false to disable blinking cursor, for users who consider it distracting. (was called: io.OptCursorBlink prior to 1.63) + bool ConfigWindowsResizeFromEdges; // = true // Enable resizing of windows from their edges and from the lower-left corner. This requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback. (This used to be a per-window ImGuiWindowFlags_ResizeFromAnySide flag) + bool ConfigWindowsMoveFromTitleBarOnly; // = false // [BETA] Set to true to only allow moving windows when clicked+dragged from the title bar. Windows without a title bar are not affected. //------------------------------------------------------------------ - // Settings (User Functions) + // Platform Functions + // (the imgui_impl_xxxx back-end files are setting those up for you) //------------------------------------------------------------------ - // Optional: access OS clipboard + // Optional: Platform/Renderer back-end name (informational only! will be displayed in About Window) + User data for back-end/wrappers to store their own stuff. + const char* BackendPlatformName; // = NULL + const char* BackendRendererName; // = NULL + void* BackendPlatformUserData; // = NULL + void* BackendRendererUserData; // = NULL + void* BackendLanguageUserData; // = NULL + + // Optional: Access OS clipboard // (default to use native Win32 clipboard on Windows, otherwise uses a private clipboard. Override to access OS clipboard on other architectures) const char* (*GetClipboardTextFn)(void* user_data); void (*SetClipboardTextFn)(void* user_data, const char* text); void* ClipboardUserData; - // Optional: notify OS Input Method Editor of the screen position of your cursor for text input position (e.g. when using Japanese/Chinese IME in Windows) + // Optional: Notify OS Input Method Editor of the screen position of your cursor for text input position (e.g. when using Japanese/Chinese IME on Windows) // (default to use native imm32 api on Windows) void (*ImeSetInputScreenPosFn)(int x, int y); - void* ImeWindowHandle; // (Windows) Set this to your HWND to get automatic IME cursor positioning. + void* ImeWindowHandle; // = NULL // (Windows) Set this to your HWND to get automatic IME cursor positioning. #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS // [OBSOLETE since 1.60+] Rendering function, will be automatically called in Render(). Please call your rendering function yourself now! // You can obtain the ImDrawData* by calling ImGui::GetDrawData() after Render(). See example applications if you are unsure of how to implement this. void (*RenderDrawListsFn)(ImDrawData* data); #else - // This is only here to keep ImGuiIO the same size, so that IMGUI_DISABLE_OBSOLETE_FUNCTIONS can exceptionally be used outside of imconfig.h. + // This is only here to keep ImGuiIO the same size/layout, so that IMGUI_DISABLE_OBSOLETE_FUNCTIONS can exceptionally be used outside of imconfig.h. void* RenderDrawListsFnUnused; #endif @@ -1161,76 +1381,150 @@ struct ImGuiIO bool KeyAlt; // Keyboard modifier pressed: Alt bool KeySuper; // Keyboard modifier pressed: Cmd/Super/Windows bool KeysDown[512]; // Keyboard keys that are pressed (ideally left in the "native" order your engine has access to keyboard keys, so you can use your own defines/enums for keys). - ImWchar InputCharacters[16+1]; // List of characters input (translated by user from keypress+keyboard state). Fill using AddInputCharacter() helper. - float NavInputs[ImGuiNavInput_COUNT]; // Gamepad inputs (keyboard keys will be auto-mapped and be written here by ImGui::NewFrame, all values will be cleared back to zero in ImGui::EndFrame) + float NavInputs[ImGuiNavInput_COUNT]; // Gamepad inputs. Cleared back to zero by EndFrame(). Keyboard keys will be auto-mapped and be written here by NewFrame(). // Functions - IMGUI_API void AddInputCharacter(ImWchar c); // Add new character into InputCharacters[] - IMGUI_API void AddInputCharactersUTF8(const char* utf8_chars); // Add new characters into InputCharacters[] from an UTF-8 string - inline void ClearInputCharacters() { InputCharacters[0] = 0; } // Clear the text input buffer manually + IMGUI_API void AddInputCharacter(ImWchar c); // Queue new character input + IMGUI_API void AddInputCharactersUTF8(const char* str); // Queue new characters input from an UTF-8 string + IMGUI_API void ClearInputCharacters(); // Clear the text input buffer manually //------------------------------------------------------------------ // Output - Retrieve after calling NewFrame() //------------------------------------------------------------------ - bool WantCaptureMouse; // When io.WantCaptureMouse is true, imgui will use the mouse inputs, do not dispatch them to your main game/application (in both cases, always pass on mouse inputs to imgui). (e.g. unclicked mouse is hovering over an imgui window, widget is active, mouse was clicked over an imgui window, etc.). - bool WantCaptureKeyboard; // When io.WantCaptureKeyboard is true, imgui will use the keyboard inputs, do not dispatch them to your main game/application (in both cases, always pass keyboard inputs to imgui). (e.g. InputText active, or an imgui window is focused and navigation is enabled, etc.). - bool WantTextInput; // Mobile/console: when io.WantTextInput is true, you may display an on-screen keyboard. This is set by ImGui when it wants textual keyboard input to happen (e.g. when a InputText widget is active). - bool WantSetMousePos; // MousePos has been altered, back-end should reposition mouse on next frame. Set only when ImGuiConfigFlags_NavEnableSetMousePos flag is enabled. - bool WantSaveIniSettings; // When manual .ini load/save is active (io.IniFilename == NULL), this will be set to notify your application that you can call SaveIniSettingsToMemory() and save yourself. IMPORTANT: You need to clear io.WantSaveIniSettings yourself. - bool NavActive; // Directional navigation is currently allowed (will handle ImGuiKey_NavXXX events) = a window is focused and it doesn't use the ImGuiWindowFlags_NoNavInputs flag. - bool NavVisible; // Directional navigation is visible and allowed (will handle ImGuiKey_NavXXX events). - float Framerate; // Application framerate estimation, in frame per second. Solely for convenience. Rolling average estimation based on IO.DeltaTime over 120 frames - int MetricsRenderVertices; // Vertices output during last call to Render() - int MetricsRenderIndices; // Indices output during last call to Render() = number of triangles * 3 - int MetricsRenderWindows; // Number of visible windows - int MetricsActiveWindows; // Number of active windows - int MetricsActiveAllocations; // Number of active allocations, updated by MemAlloc/MemFree based on current context. May be off if you have multiple imgui contexts. - ImVec2 MouseDelta; // Mouse delta. Note that this is zero if either current or previous position are invalid (-FLT_MAX,-FLT_MAX), so a disappearing/reappearing mouse won't have a huge delta. + bool WantCaptureMouse; // When io.WantCaptureMouse is true, imgui will use the mouse inputs, do not dispatch them to your main game/application (in both cases, always pass on mouse inputs to imgui). (e.g. unclicked mouse is hovering over an imgui window, widget is active, mouse was clicked over an imgui window, etc.). + bool WantCaptureKeyboard; // When io.WantCaptureKeyboard is true, imgui will use the keyboard inputs, do not dispatch them to your main game/application (in both cases, always pass keyboard inputs to imgui). (e.g. InputText active, or an imgui window is focused and navigation is enabled, etc.). + bool WantTextInput; // Mobile/console: when io.WantTextInput is true, you may display an on-screen keyboard. This is set by ImGui when it wants textual keyboard input to happen (e.g. when a InputText widget is active). + bool WantSetMousePos; // MousePos has been altered, back-end should reposition mouse on next frame. Set only when ImGuiConfigFlags_NavEnableSetMousePos flag is enabled. + bool WantSaveIniSettings; // When manual .ini load/save is active (io.IniFilename == NULL), this will be set to notify your application that you can call SaveIniSettingsToMemory() and save yourself. IMPORTANT: You need to clear io.WantSaveIniSettings yourself. + bool NavActive; // Directional navigation is currently allowed (will handle ImGuiKey_NavXXX events) = a window is focused and it doesn't use the ImGuiWindowFlags_NoNavInputs flag. + bool NavVisible; // Directional navigation is visible and allowed (will handle ImGuiKey_NavXXX events). + float Framerate; // Application framerate estimation, in frame per second. Solely for convenience. Rolling average estimation based on IO.DeltaTime over 120 frames + int MetricsRenderVertices; // Vertices output during last call to Render() + int MetricsRenderIndices; // Indices output during last call to Render() = number of triangles * 3 + int MetricsRenderWindows; // Number of visible windows + int MetricsActiveWindows; // Number of active windows + int MetricsActiveAllocations; // Number of active allocations, updated by MemAlloc/MemFree based on current context. May be off if you have multiple imgui contexts. + ImVec2 MouseDelta; // Mouse delta. Note that this is zero if either current or previous position are invalid (-FLT_MAX,-FLT_MAX), so a disappearing/reappearing mouse won't have a huge delta. //------------------------------------------------------------------ // [Internal] ImGui will maintain those fields. Forward compatibility not guaranteed! //------------------------------------------------------------------ - ImVec2 MousePosPrev; // Previous mouse position temporary storage (nb: not for public use, set to MousePos in NewFrame()) - ImVec2 MouseClickedPos[5]; // Position at time of clicking - double MouseClickedTime[5]; // Time of last click (used to figure out double-click) - bool MouseClicked[5]; // Mouse button went from !Down to Down - bool MouseDoubleClicked[5]; // Has mouse button been double-clicked? - bool MouseReleased[5]; // Mouse button went from Down to !Down - bool MouseDownOwned[5]; // Track if button was clicked inside a window. We don't request mouse capture from the application if click started outside ImGui bounds. - float MouseDownDuration[5]; // Duration the mouse button has been down (0.0f == just clicked) - float MouseDownDurationPrev[5]; // Previous time the mouse button has been down - ImVec2 MouseDragMaxDistanceAbs[5]; // Maximum distance, absolute, on each axis, of how much mouse has traveled from the clicking point - float MouseDragMaxDistanceSqr[5]; // Squared maximum distance of how much mouse has traveled from the clicking point - float KeysDownDuration[512]; // Duration the keyboard key has been down (0.0f == just pressed) - float KeysDownDurationPrev[512]; // Previous duration the key has been down + ImVec2 MousePosPrev; // Previous mouse position (note that MouseDelta is not necessary == MousePos-MousePosPrev, in case either position is invalid) + ImVec2 MouseClickedPos[5]; // Position at time of clicking + double MouseClickedTime[5]; // Time of last click (used to figure out double-click) + bool MouseClicked[5]; // Mouse button went from !Down to Down + bool MouseDoubleClicked[5]; // Has mouse button been double-clicked? + bool MouseReleased[5]; // Mouse button went from Down to !Down + bool MouseDownOwned[5]; // Track if button was clicked inside a window. We don't request mouse capture from the application if click started outside ImGui bounds. + float MouseDownDuration[5]; // Duration the mouse button has been down (0.0f == just clicked) + float MouseDownDurationPrev[5]; // Previous time the mouse button has been down + ImVec2 MouseDragMaxDistanceAbs[5]; // Maximum distance, absolute, on each axis, of how much mouse has traveled from the clicking point + float MouseDragMaxDistanceSqr[5]; // Squared maximum distance of how much mouse has traveled from the clicking point + float KeysDownDuration[512]; // Duration the keyboard key has been down (0.0f == just pressed) + float KeysDownDurationPrev[512]; // Previous duration the key has been down float NavInputsDownDuration[ImGuiNavInput_COUNT]; float NavInputsDownDurationPrev[ImGuiNavInput_COUNT]; + ImVector InputQueueCharacters; // Queue of _characters_ input (obtained by platform back-end). Fill using AddInputCharacter() helper. IMGUI_API ImGuiIO(); }; +//----------------------------------------------------------------------------- +// Misc data structures +//----------------------------------------------------------------------------- + +// Shared state of InputText(), passed as an argument to your callback when a ImGuiInputTextFlags_Callback* flag is used. +// The callback function should return 0 by default. +// Callbacks (follow a flag name and see comments in ImGuiInputTextFlags_ declarations for more details) +// - ImGuiInputTextFlags_CallbackCompletion: Callback on pressing TAB +// - ImGuiInputTextFlags_CallbackHistory: Callback on pressing Up/Down arrows +// - ImGuiInputTextFlags_CallbackAlways: Callback on each iteration +// - ImGuiInputTextFlags_CallbackCharFilter: Callback on character inputs to replace or discard them. Modify 'EventChar' to replace or discard, or return 1 in callback to discard. +// - ImGuiInputTextFlags_CallbackResize: Callback on buffer capacity changes request (beyond 'buf_size' parameter value), allowing the string to grow. +struct ImGuiInputTextCallbackData +{ + ImGuiInputTextFlags EventFlag; // One ImGuiInputTextFlags_Callback* // Read-only + ImGuiInputTextFlags Flags; // What user passed to InputText() // Read-only + void* UserData; // What user passed to InputText() // Read-only + + // Arguments for the different callback events + // - To modify the text buffer in a callback, prefer using the InsertChars() / DeleteChars() function. InsertChars() will take care of calling the resize callback if necessary. + // - If you know your edits are not going to resize the underlying buffer allocation, you may modify the contents of 'Buf[]' directly. You need to update 'BufTextLen' accordingly (0 <= BufTextLen < BufSize) and set 'BufDirty'' to true so InputText can update its internal state. + ImWchar EventChar; // Character input // Read-write // [CharFilter] Replace character with another one, or set to zero to drop. return 1 is equivalent to setting EventChar=0; + ImGuiKey EventKey; // Key pressed (Up/Down/TAB) // Read-only // [Completion,History] + char* Buf; // Text buffer // Read-write // [Resize] Can replace pointer / [Completion,History,Always] Only write to pointed data, don't replace the actual pointer! + int BufTextLen; // Text length (in bytes) // Read-write // [Resize,Completion,History,Always] Exclude zero-terminator storage. In C land: == strlen(some_text), in C++ land: string.length() + int BufSize; // Buffer size (in bytes) = capacity+1 // Read-only // [Resize,Completion,History,Always] Include zero-terminator storage. In C land == ARRAYSIZE(my_char_array), in C++ land: string.capacity()+1 + bool BufDirty; // Set if you modify Buf/BufTextLen! // Write // [Completion,History,Always] + int CursorPos; // // Read-write // [Completion,History,Always] + int SelectionStart; // // Read-write // [Completion,History,Always] == to SelectionEnd when no selection) + int SelectionEnd; // // Read-write // [Completion,History,Always] + + // Helper functions for text manipulation. + // Use those function to benefit from the CallbackResize behaviors. Calling those function reset the selection. + IMGUI_API ImGuiInputTextCallbackData(); + IMGUI_API void DeleteChars(int pos, int bytes_count); + IMGUI_API void InsertChars(int pos, const char* text, const char* text_end = NULL); + bool HasSelection() const { return SelectionStart != SelectionEnd; } +}; + +// Resizing callback data to apply custom constraint. As enabled by SetNextWindowSizeConstraints(). Callback is called during the next Begin(). +// NB: For basic min/max size constraint on each axis you don't need to use the callback! The SetNextWindowSizeConstraints() parameters are enough. +struct ImGuiSizeCallbackData +{ + void* UserData; // Read-only. What user passed to SetNextWindowSizeConstraints() + ImVec2 Pos; // Read-only. Window position, for reference. + ImVec2 CurrentSize; // Read-only. Current window size. + ImVec2 DesiredSize; // Read-write. Desired size, based on user's mouse position. Write to this field to restrain resizing. +}; + +// Data payload for Drag and Drop operations: AcceptDragDropPayload(), GetDragDropPayload() +struct ImGuiPayload +{ + // Members + void* Data; // Data (copied and owned by dear imgui) + int DataSize; // Data size + + // [Internal] + ImGuiID SourceId; // Source item id + ImGuiID SourceParentId; // Source parent id (if available) + int DataFrameCount; // Data timestamp + char DataType[32+1]; // Data type tag (short user-supplied string, 32 characters max) + bool Preview; // Set when AcceptDragDropPayload() was called and mouse has been hovering the target item (nb: handle overlapping drag targets) + bool Delivery; // Set when AcceptDragDropPayload() was called and mouse button is released over the target item. + + ImGuiPayload() { Clear(); } + void Clear() { SourceId = SourceParentId = 0; Data = NULL; DataSize = 0; memset(DataType, 0, sizeof(DataType)); DataFrameCount = -1; Preview = Delivery = false; } + bool IsDataType(const char* type) const { return DataFrameCount != -1 && strcmp(type, DataType) == 0; } + bool IsPreview() const { return Preview; } + bool IsDelivery() const { return Delivery; } +}; + //----------------------------------------------------------------------------- // Obsolete functions (Will be removed! Read 'API BREAKING CHANGES' section in imgui.cpp for details) +// Please keep your copy of dear imgui up to date! Occasionally set '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' in imconfig.h to stay ahead. //----------------------------------------------------------------------------- #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS namespace ImGui { + // OBSOLETED in 1.69 (from Mar 2019) + static inline ImDrawList* GetOverlayDrawList() { return GetForegroundDrawList(); } // OBSOLETED in 1.66 (from Sep 2018) static inline void SetScrollHere(float center_ratio=0.5f){ SetScrollHereY(center_ratio); } - // OBSOLETED in 1.63 (from Aug 2018) + // OBSOLETED in 1.63 (between Aug 2018 and Sept 2018) static inline bool IsItemDeactivatedAfterChange() { return IsItemDeactivatedAfterEdit(); } - // OBSOLETED in 1.61 (from Apr 2018) - IMGUI_API bool InputFloat(const char* label, float* v, float step, float step_fast, int decimal_precision, ImGuiInputTextFlags extra_flags = 0); // Use the 'const char* format' version instead of 'decimal_precision'! - IMGUI_API bool InputFloat2(const char* label, float v[2], int decimal_precision, ImGuiInputTextFlags extra_flags = 0); - IMGUI_API bool InputFloat3(const char* label, float v[3], int decimal_precision, ImGuiInputTextFlags extra_flags = 0); - IMGUI_API bool InputFloat4(const char* label, float v[4], int decimal_precision, ImGuiInputTextFlags extra_flags = 0); - // OBSOLETED in 1.60 (from Dec 2017) + // OBSOLETED in 1.61 (between Apr 2018 and Aug 2018) + IMGUI_API bool InputFloat(const char* label, float* v, float step, float step_fast, int decimal_precision, ImGuiInputTextFlags flags = 0); // Use the 'const char* format' version instead of 'decimal_precision'! + IMGUI_API bool InputFloat2(const char* label, float v[2], int decimal_precision, ImGuiInputTextFlags flags = 0); + IMGUI_API bool InputFloat3(const char* label, float v[3], int decimal_precision, ImGuiInputTextFlags flags = 0); + IMGUI_API bool InputFloat4(const char* label, float v[4], int decimal_precision, ImGuiInputTextFlags flags = 0); + // OBSOLETED in 1.60 (between Dec 2017 and Apr 2018) static inline bool IsAnyWindowFocused() { return IsWindowFocused(ImGuiFocusedFlags_AnyWindow); } static inline bool IsAnyWindowHovered() { return IsWindowHovered(ImGuiHoveredFlags_AnyWindow); } - static inline ImVec2 CalcItemRectClosestPoint(const ImVec2& pos, bool on_edge = false, float outward = 0.f) { (void)on_edge; (void)outward; IM_ASSERT(0); return pos; } + static inline ImVec2 CalcItemRectClosestPoint(const ImVec2& pos, bool on_edge = false, float outward = 0.f) { IM_UNUSED(on_edge); IM_UNUSED(outward); IM_ASSERT(0); return pos; } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) static inline void ShowTestWindow() { return ShowDemoWindow(); } static inline bool IsRootWindowFocused() { return IsWindowFocused(ImGuiFocusedFlags_RootWindow); } @@ -1248,83 +1542,20 @@ namespace ImGui static inline bool IsMouseHoveringAnyWindow() { return IsWindowHovered(ImGuiHoveredFlags_AnyWindow); } static inline bool IsMouseHoveringWindow() { return IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem); } } +typedef ImGuiInputTextCallback ImGuiTextEditCallback; // OBSOLETE in 1.63 (from Aug 2018): made the names consistent +typedef ImGuiInputTextCallbackData ImGuiTextEditCallbackData; #endif //----------------------------------------------------------------------------- // Helpers //----------------------------------------------------------------------------- -// Helper: Lightweight std::vector<> like class to avoid dragging dependencies (also: Windows implementation of STL with debug enabled is absurdly slow, so let's bypass it so our code runs fast in debug). -// *Important* Our implementation does NOT call C++ constructors/destructors. This is intentional, we do not require it but you have to be mindful of that. Do _not_ use this class as a std::vector replacement in your code! -template -class ImVector -{ -public: - int Size; - int Capacity; - T* Data; - - typedef T value_type; - typedef value_type* iterator; - typedef const value_type* const_iterator; - - inline ImVector() { Size = Capacity = 0; Data = NULL; } - inline ~ImVector() { if (Data) ImGui::MemFree(Data); } - inline ImVector(const ImVector& src) { Size = Capacity = 0; Data = NULL; operator=(src); } - inline ImVector& operator=(const ImVector& src) { clear(); resize(src.Size); memcpy(Data, src.Data, (size_t)Size * sizeof(value_type)); return *this; } - - inline bool empty() const { return Size == 0; } - inline int size() const { return Size; } - inline int capacity() const { return Capacity; } - inline value_type& operator[](int i) { IM_ASSERT(i < Size); return Data[i]; } - inline const value_type& operator[](int i) const { IM_ASSERT(i < Size); return Data[i]; } - - inline void clear() { if (Data) { Size = Capacity = 0; ImGui::MemFree(Data); Data = NULL; } } - inline iterator begin() { return Data; } - inline const_iterator begin() const { return Data; } - inline iterator end() { return Data + Size; } - inline const_iterator end() const { return Data + Size; } - inline value_type& front() { IM_ASSERT(Size > 0); return Data[0]; } - inline const value_type& front() const { IM_ASSERT(Size > 0); return Data[0]; } - inline value_type& back() { IM_ASSERT(Size > 0); return Data[Size - 1]; } - inline const value_type& back() const { IM_ASSERT(Size > 0); return Data[Size - 1]; } - inline void swap(ImVector& rhs) { int rhs_size = rhs.Size; rhs.Size = Size; Size = rhs_size; int rhs_cap = rhs.Capacity; rhs.Capacity = Capacity; Capacity = rhs_cap; value_type* rhs_data = rhs.Data; rhs.Data = Data; Data = rhs_data; } - - inline int _grow_capacity(int sz) const { int new_capacity = Capacity ? (Capacity + Capacity/2) : 8; return new_capacity > sz ? new_capacity : sz; } - inline void resize(int new_size) { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; } - inline void resize(int new_size,const value_type& v){ if (new_size > Capacity) reserve(_grow_capacity(new_size)); if (new_size > Size) for (int n = Size; n < new_size; n++) memcpy(&Data[n], &v, sizeof(v)); Size = new_size; } - inline void reserve(int new_capacity) - { - if (new_capacity <= Capacity) - return; - value_type* new_data = (value_type*)ImGui::MemAlloc((size_t)new_capacity * sizeof(value_type)); - if (Data) - { - memcpy(new_data, Data, (size_t)Size * sizeof(value_type)); - ImGui::MemFree(Data); - } - Data = new_data; - Capacity = new_capacity; - } - - // NB: It is forbidden to call push_back/push_front/insert with a reference pointing inside the ImVector data itself! e.g. v.push_back(v[10]) is forbidden. - inline void push_back(const value_type& v) { if (Size == Capacity) reserve(_grow_capacity(Size + 1)); memcpy(&Data[Size], &v, sizeof(v)); Size++; } - inline void pop_back() { IM_ASSERT(Size > 0); Size--; } - inline void push_front(const value_type& v) { if (Size == 0) push_back(v); else insert(Data, v); } - inline iterator erase(const_iterator it) { IM_ASSERT(it >= Data && it < Data+Size); const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + 1, ((size_t)Size - (size_t)off - 1) * sizeof(value_type)); Size--; return Data + off; } - inline iterator erase(const_iterator it, const_iterator it_last){ IM_ASSERT(it >= Data && it < Data+Size && it_last > it && it_last <= Data+Size); const ptrdiff_t count = it_last - it; const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + count, ((size_t)Size - (size_t)off - count) * sizeof(value_type)); Size -= (int)count; return Data + off; } - inline iterator erase_unsorted(const_iterator it) { IM_ASSERT(it >= Data && it < Data+Size); const ptrdiff_t off = it - Data; if (it < Data+Size-1) memcpy(Data + off, Data + Size - 1, sizeof(value_type)); Size--; return Data + off; } - inline iterator insert(const_iterator it, const value_type& v) { IM_ASSERT(it >= Data && it <= Data+Size); const ptrdiff_t off = it - Data; if (Size == Capacity) reserve(_grow_capacity(Size + 1)); if (off < (int)Size) memmove(Data + off + 1, Data + off, ((size_t)Size - (size_t)off) * sizeof(value_type)); memcpy(&Data[off], &v, sizeof(v)); Size++; return Data + off; } - inline bool contains(const value_type& v) const { const T* data = Data; const T* data_end = Data + Size; while (data < data_end) if (*data++ == v) return true; return false; } - inline int index_from_pointer(const_iterator it) const { IM_ASSERT(it >= Data && it <= Data+Size); const ptrdiff_t off = it - Data; return (int)off; } -}; - // Helper: IM_NEW(), IM_PLACEMENT_NEW(), IM_DELETE() macros to call MemAlloc + Placement New, Placement Delete + MemFree // We call C++ constructor on own allocated memory via the placement "new(ptr) Type()" syntax. // Defining a custom placement new() with a dummy parameter allows us to bypass including which on some platforms complains when user has disabled exceptions. struct ImNewDummy {}; inline void* operator new(size_t, ImNewDummy, void* ptr) { return ptr; } -inline void operator delete(void*, ImNewDummy, void*) {} // This is only required so we can use the symetrical new() +inline void operator delete(void*, ImNewDummy, void*) {} // This is only required so we can use the symmetrical new() #define IM_PLACEMENT_NEW(_PTR) new(ImNewDummy(), _PTR) #define IM_NEW(_TYPE) new(ImNewDummy(), ImGui::MemAlloc(sizeof(_TYPE))) _TYPE template void IM_DELETE(T* p) { if (p) { p->~T(); ImGui::MemFree(p); } } @@ -1339,15 +1570,15 @@ struct ImGuiOnceUponAFrame }; // Helper: Macro for ImGuiOnceUponAFrame. Attention: The macro expands into 2 statement so make sure you don't use it within e.g. an if() statement without curly braces. -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS // Will obsolete -#define IMGUI_ONCE_UPON_A_FRAME static ImGuiOnceUponAFrame imgui_oaf; if (imgui_oaf) +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS +#define IMGUI_ONCE_UPON_A_FRAME static ImGuiOnceUponAFrame imgui_oaf; if (imgui_oaf) // OBSOLETED in 1.51, will remove! #endif // Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]" struct ImGuiTextFilter { IMGUI_API ImGuiTextFilter(const char* default_filter = ""); - IMGUI_API bool Draw(const char* label = "Filter (inc,-exc)", float width = 0.0f); // Helper calling InputText+Build + IMGUI_API bool Draw(const char* label = "Filter (inc,-exc)", float width = 0.0f); // Helper calling InputText+Build IMGUI_API bool PassFilter(const char* text, const char* text_end = NULL) const; IMGUI_API void Build(); void Clear() { InputBuf[0] = 0; Build(); } @@ -1382,11 +1613,12 @@ struct ImGuiTextBuffer inline char operator[](int i) { IM_ASSERT(Buf.Data != NULL); return Buf.Data[i]; } const char* begin() const { return Buf.Data ? &Buf.front() : EmptyString; } const char* end() const { return Buf.Data ? &Buf.back() : EmptyString; } // Buf is zero-terminated, so end() will point on the zero-terminator - int size() const { return Buf.Data ? Buf.Size - 1 : 0; } + int size() const { return Buf.Size ? Buf.Size - 1 : 0; } bool empty() { return Buf.Size <= 1; } void clear() { Buf.clear(); } void reserve(int capacity) { Buf.reserve(capacity); } const char* c_str() const { return Buf.Data ? Buf.Data : EmptyString; } + IMGUI_API void append(const char* str, const char* str_end = NULL); IMGUI_API void appendf(const char* fmt, ...) IM_FMTARGS(2); IMGUI_API void appendfv(const char* fmt, va_list args) IM_FMTLIST(2); }; @@ -1440,76 +1672,34 @@ struct ImGuiStorage IMGUI_API void BuildSortByKey(); }; -// Shared state of InputText(), passed as an argument to your callback when a ImGuiInputTextFlags_Callback* flag is used. -// The callback function should return 0 by default. -// Callbacks (follow a flag name and see comments in ImGuiInputTextFlags_ declarations for more details) -// - ImGuiInputTextFlags_CallbackCompletion: Callback on pressing TAB -// - ImGuiInputTextFlags_CallbackHistory: Callback on pressing Up/Down arrows -// - ImGuiInputTextFlags_CallbackAlways: Callback on each iteration -// - ImGuiInputTextFlags_CallbackCharFilter: Callback on character inputs to replace or discard them. Modify 'EventChar' to replace or discard, or return 1 in callback to discard. -// - ImGuiInputTextFlags_CallbackResize: Callback on buffer capacity changes request (beyond 'buf_size' parameter value), allowing the string to grow. -struct ImGuiInputTextCallbackData -{ - ImGuiInputTextFlags EventFlag; // One ImGuiInputTextFlags_Callback* // Read-only - ImGuiInputTextFlags Flags; // What user passed to InputText() // Read-only - void* UserData; // What user passed to InputText() // Read-only - - // Arguments for the different callback events - // - To modify the text buffer in a callback, prefer using the InsertChars() / DeleteChars() function. InsertChars() will take care of calling the resize callback if necessary. - // - If you know your edits are not going to resize the underlying buffer allocation, you may modify the contents of 'Buf[]' directly. You need to update 'BufTextLen' accordingly (0 <= BufTextLen < BufSize) and set 'BufDirty'' to true so InputText can update its internal state. - ImWchar EventChar; // Character input // Read-write // [CharFilter] Replace character with another one, or set to zero to drop. return 1 is equivalent to setting EventChar=0; - ImGuiKey EventKey; // Key pressed (Up/Down/TAB) // Read-only // [Completion,History] - char* Buf; // Text buffer // Read-write // [Resize] Can replace pointer / [Completion,History,Always] Only write to pointed data, don't replace the actual pointer! - int BufTextLen; // Text length (in bytes) // Read-write // [Resize,Completion,History,Always] Exclude zero-terminator storage. In C land: == strlen(some_text), in C++ land: string.length() - int BufSize; // Buffer size (in bytes) = capacity+1 // Read-only // [Resize,Completion,History,Always] Include zero-terminator storage. In C land == ARRAYSIZE(my_char_array), in C++ land: string.capacity()+1 - bool BufDirty; // Set if you modify Buf/BufTextLen! // Write // [Completion,History,Always] - int CursorPos; // // Read-write // [Completion,History,Always] - int SelectionStart; // // Read-write // [Completion,History,Always] == to SelectionEnd when no selection) - int SelectionEnd; // // Read-write // [Completion,History,Always] - - // Helper functions for text manipulation. - // Use those function to benefit from the CallbackResize behaviors. Calling those function reset the selection. - IMGUI_API ImGuiInputTextCallbackData(); - IMGUI_API void DeleteChars(int pos, int bytes_count); - IMGUI_API void InsertChars(int pos, const char* text, const char* text_end = NULL); - bool HasSelection() const { return SelectionStart != SelectionEnd; } -}; - -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS -typedef ImGuiInputTextCallback ImGuiTextEditCallback; // [OBSOLETE 1.63+] Made the names consistent -typedef ImGuiInputTextCallbackData ImGuiTextEditCallbackData; -#endif - -// Resizing callback data to apply custom constraint. As enabled by SetNextWindowSizeConstraints(). Callback is called during the next Begin(). -// NB: For basic min/max size constraint on each axis you don't need to use the callback! The SetNextWindowSizeConstraints() parameters are enough. -struct ImGuiSizeCallbackData -{ - void* UserData; // Read-only. What user passed to SetNextWindowSizeConstraints() - ImVec2 Pos; // Read-only. Window position, for reference. - ImVec2 CurrentSize; // Read-only. Current window size. - ImVec2 DesiredSize; // Read-write. Desired size, based on user's mouse position. Write to this field to restrain resizing. -}; - -// Data payload for Drag and Drop operations -struct ImGuiPayload +// Helper: Manually clip large list of items. +// If you are submitting lots of evenly spaced items and you have a random access to the list, you can perform coarse clipping based on visibility to save yourself from processing those items at all. +// The clipper calculates the range of visible items and advance the cursor to compensate for the non-visible items we have skipped. +// ImGui already clip items based on their bounds but it needs to measure text size to do so. Coarse clipping before submission makes this cost and your own data fetching/submission cost null. +// Usage: +// ImGuiListClipper clipper(1000); // we have 1000 elements, evenly spaced. +// while (clipper.Step()) +// for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) +// ImGui::Text("line number %d", i); +// - Step 0: the clipper let you process the first element, regardless of it being visible or not, so we can measure the element height (step skipped if we passed a known height as second arg to constructor). +// - Step 1: the clipper infer height from first element, calculate the actual range of elements to display, and position the cursor before the first element. +// - (Step 2: dummy step only required if an explicit items_height was passed to constructor or Begin() and user call Step(). Does nothing and switch to Step 3.) +// - Step 3: the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd), advance the cursor to the end of the list and then returns 'false' to end the loop. +struct ImGuiListClipper { - // Members - void* Data; // Data (copied and owned by dear imgui) - int DataSize; // Data size + float StartPosY; + float ItemsHeight; + int ItemsCount, StepNo, DisplayStart, DisplayEnd; - // [Internal] - ImGuiID SourceId; // Source item id - ImGuiID SourceParentId; // Source parent id (if available) - int DataFrameCount; // Data timestamp - char DataType[32+1]; // Data type tag (short user-supplied string, 32 characters max) - bool Preview; // Set when AcceptDragDropPayload() was called and mouse has been hovering the target item (nb: handle overlapping drag targets) - bool Delivery; // Set when AcceptDragDropPayload() was called and mouse button is released over the target item. + // items_count: Use -1 to ignore (you can call Begin later). Use INT_MAX if you don't know how many items you have (in which case the cursor won't be advanced in the final step). + // items_height: Use -1.0f to be calculated automatically on first step. Otherwise pass in the distance between your items, typically GetTextLineHeightWithSpacing() or GetFrameHeightWithSpacing(). + // If you don't specify an items_height, you NEED to call Step(). If you specify items_height you may call the old Begin()/End() api directly, but prefer calling Step(). + ImGuiListClipper(int items_count = -1, float items_height = -1.0f) { Begin(items_count, items_height); } // NB: Begin() initialize every fields (as we allow user to call Begin/End multiple times on a same instance if they want). + ~ImGuiListClipper() { IM_ASSERT(ItemsCount == -1); } // Assert if user forgot to call End() or Step() until false. - ImGuiPayload() { Clear(); } - void Clear() { SourceId = SourceParentId = 0; Data = NULL; DataSize = 0; memset(DataType, 0, sizeof(DataType)); DataFrameCount = -1; Preview = Delivery = false; } - bool IsDataType(const char* type) const { return DataFrameCount != -1 && strcmp(type, DataType) == 0; } - bool IsPreview() const { return Preview; } - bool IsDelivery() const { return Delivery; } + IMGUI_API bool Step(); // Call until it returns false. The DisplayStart/DisplayEnd fields will be set and you can process/draw those items. + IMGUI_API void Begin(int items_count, float items_height = -1.0f); // Automatically called by constructor if you passed 'items_count' or by Step() in Step 1. + IMGUI_API void End(); // Automatically called on the last call of Step() that returns false. }; // Helpers macros to generate 32-bits encoded colors @@ -1531,7 +1721,7 @@ struct ImGuiPayload #define IM_COL32_BLACK IM_COL32(0,0,0,255) // Opaque black #define IM_COL32_BLACK_TRANS IM_COL32(0,0,0,0) // Transparent black = 0x00000000 -// Helper: ImColor() implicity converts colors to either ImU32 (packed 4x1 byte) or ImVec4 (4x1 float) +// Helper: ImColor() implicitly converts colors to either ImU32 (packed 4x1 byte) or ImVec4 (4x1 float) // Prefer using IM_COL32() macros if you want a guaranteed compile-time ImU32 for usage with ImDrawList API. // **Avoid storing ImColor! Store either u32 of ImVec4. This is not a full-featured color class. MAY OBSOLETE. // **None of the ImGui API are using ImColor directly but you can use it as a convenience to pass colors in either ImU32 or ImVec4 formats. Explicitly cast to ImU32 or ImVec4 if needed. @@ -1552,45 +1742,16 @@ struct ImColor static ImColor HSV(float h, float s, float v, float a = 1.0f) { float r,g,b; ImGui::ColorConvertHSVtoRGB(h, s, v, r, g, b); return ImColor(r,g,b,a); } }; -// Helper: Manually clip large list of items. -// If you are submitting lots of evenly spaced items and you have a random access to the list, you can perform coarse clipping based on visibility to save yourself from processing those items at all. -// The clipper calculates the range of visible items and advance the cursor to compensate for the non-visible items we have skipped. -// ImGui already clip items based on their bounds but it needs to measure text size to do so. Coarse clipping before submission makes this cost and your own data fetching/submission cost null. -// Usage: -// ImGuiListClipper clipper(1000); // we have 1000 elements, evenly spaced. -// while (clipper.Step()) -// for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) -// ImGui::Text("line number %d", i); -// - Step 0: the clipper let you process the first element, regardless of it being visible or not, so we can measure the element height (step skipped if we passed a known height as second arg to constructor). -// - Step 1: the clipper infer height from first element, calculate the actual range of elements to display, and position the cursor before the first element. -// - (Step 2: dummy step only required if an explicit items_height was passed to constructor or Begin() and user call Step(). Does nothing and switch to Step 3.) -// - Step 3: the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd), advance the cursor to the end of the list and then returns 'false' to end the loop. -struct ImGuiListClipper -{ - float StartPosY; - float ItemsHeight; - int ItemsCount, StepNo, DisplayStart, DisplayEnd; - - // items_count: Use -1 to ignore (you can call Begin later). Use INT_MAX if you don't know how many items you have (in which case the cursor won't be advanced in the final step). - // items_height: Use -1.0f to be calculated automatically on first step. Otherwise pass in the distance between your items, typically GetTextLineHeightWithSpacing() or GetFrameHeightWithSpacing(). - // If you don't specify an items_height, you NEED to call Step(). If you specify items_height you may call the old Begin()/End() api directly, but prefer calling Step(). - ImGuiListClipper(int items_count = -1, float items_height = -1.0f) { Begin(items_count, items_height); } // NB: Begin() initialize every fields (as we allow user to call Begin/End multiple times on a same instance if they want). - ~ImGuiListClipper() { IM_ASSERT(ItemsCount == -1); } // Assert if user forgot to call End() or Step() until false. - - IMGUI_API bool Step(); // Call until it returns false. The DisplayStart/DisplayEnd fields will be set and you can process/draw those items. - IMGUI_API void Begin(int items_count, float items_height = -1.0f); // Automatically called by constructor if you passed 'items_count' or by Step() in Step 1. - IMGUI_API void End(); // Automatically called on the last call of Step() that returns false. -}; - //----------------------------------------------------------------------------- -// Draw List +// Draw List API (ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListFlags, ImDrawList, ImDrawData) // Hold a series of drawing commands. The user provides a renderer for ImDrawData which essentially contains an array of ImDrawList. //----------------------------------------------------------------------------- // Draw callbacks for advanced uses. -// NB- You most likely do NOT need to use draw callbacks just to create your own widget or customized UI rendering (you can poke into the draw list for that) -// Draw callback may be useful for example, A) Change your GPU render state, B) render a complex 3D scene inside a UI element (without an intermediate texture/render target), etc. -// The expected behavior from your rendering function is 'if (cmd.UserCallback != NULL) cmd.UserCallback(parent_list, cmd); else RenderTriangles()' +// NB: You most likely do NOT need to use draw callbacks just to create your own widget or customized UI rendering, +// you can poke into the draw list for that! Draw callback may be useful for example to: A) Change your GPU render state, +// B) render a complex 3D scene inside a UI element without an intermediate texture/render target, etc. +// The expected behavior from your rendering function is 'if (cmd.UserCallback != NULL) { cmd.UserCallback(parent_list, cmd); } else { RenderTriangles() }' typedef void (*ImDrawCallback)(const ImDrawList* parent_list, const ImDrawCmd* cmd); // Typically, 1 command = 1 GPU draw call (unless command is a callback) @@ -1605,7 +1766,7 @@ struct ImDrawCmd ImDrawCmd() { ElemCount = 0; ClipRect.x = ClipRect.y = ClipRect.z = ClipRect.w = 0.0f; TextureId = (ImTextureID)NULL; UserCallback = NULL; UserCallbackData = NULL; } }; -// Vertex index (override with '#define ImDrawIdx unsigned int' inside in imconfig.h) +// Vertex index (override with '#define ImDrawIdx unsigned int' in imconfig.h) #ifndef ImDrawIdx typedef unsigned short ImDrawIdx; #endif @@ -1649,8 +1810,9 @@ enum ImDrawCornerFlags_ enum ImDrawListFlags_ { - ImDrawListFlags_AntiAliasedLines = 1 << 0, - ImDrawListFlags_AntiAliasedFill = 1 << 1 + ImDrawListFlags_None = 0, + ImDrawListFlags_AntiAliasedLines = 1 << 0, // Lines are anti-aliased (*2 the number of triangles for 1.0f wide line, otherwise *3 the number of triangles) + ImDrawListFlags_AntiAliasedFill = 1 << 1 // Filled shapes have anti-aliased edges (*2 the number of vertices) }; // Draw command list @@ -1707,16 +1869,16 @@ struct ImDrawList IMGUI_API void AddImage(ImTextureID user_texture_id, const ImVec2& a, const ImVec2& b, const ImVec2& uv_a = ImVec2(0,0), const ImVec2& uv_b = ImVec2(1,1), ImU32 col = 0xFFFFFFFF); IMGUI_API void AddImageQuad(ImTextureID user_texture_id, const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, const ImVec2& uv_a = ImVec2(0,0), const ImVec2& uv_b = ImVec2(1,0), const ImVec2& uv_c = ImVec2(1,1), const ImVec2& uv_d = ImVec2(0,1), ImU32 col = 0xFFFFFFFF); IMGUI_API void AddImageRounded(ImTextureID user_texture_id, const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, ImU32 col, float rounding, int rounding_corners = ImDrawCornerFlags_All); - IMGUI_API void AddPolyline(const ImVec2* points, const int num_points, ImU32 col, bool closed, float thickness); - IMGUI_API void AddConvexPolyFilled(const ImVec2* points, const int num_points, ImU32 col); // Note: Anti-aliased filling requires points to be in clockwise order. + IMGUI_API void AddPolyline(const ImVec2* points, int num_points, ImU32 col, bool closed, float thickness); + IMGUI_API void AddConvexPolyFilled(const ImVec2* points, int num_points, ImU32 col); // Note: Anti-aliased filling requires points to be in clockwise order. IMGUI_API void AddBezierCurve(const ImVec2& pos0, const ImVec2& cp0, const ImVec2& cp1, const ImVec2& pos1, ImU32 col, float thickness, int num_segments = 0); // Stateful path API, add points then finish with PathFillConvex() or PathStroke() - inline void PathClear() { _Path.resize(0); } + inline void PathClear() { _Path.Size = 0; } inline void PathLineTo(const ImVec2& pos) { _Path.push_back(pos); } - inline void PathLineToMergeDuplicate(const ImVec2& pos) { if (_Path.Size == 0 || memcmp(&_Path[_Path.Size-1], &pos, 8) != 0) _Path.push_back(pos); } - inline void PathFillConvex(ImU32 col) { AddConvexPolyFilled(_Path.Data, _Path.Size, col); PathClear(); } // Note: Anti-aliased filling requires points to be in clockwise order. - inline void PathStroke(ImU32 col, bool closed, float thickness = 1.0f) { AddPolyline(_Path.Data, _Path.Size, col, closed, thickness); PathClear(); } + inline void PathLineToMergeDuplicate(const ImVec2& pos) { if (_Path.Size == 0 || memcmp(&_Path.Data[_Path.Size-1], &pos, 8) != 0) _Path.push_back(pos); } + inline void PathFillConvex(ImU32 col) { AddConvexPolyFilled(_Path.Data, _Path.Size, col); _Path.Size = 0; } // Note: Anti-aliased filling requires points to be in clockwise order. + inline void PathStroke(ImU32 col, bool closed, float thickness = 1.0f) { AddPolyline(_Path.Data, _Path.Size, col, closed, thickness); _Path.Size = 0; } IMGUI_API void PathArcTo(const ImVec2& centre, float radius, float a_min, float a_max, int num_segments = 10); IMGUI_API void PathArcToFast(const ImVec2& centre, float radius, int a_min_of_12, int a_max_of_12); // Use precomputed angles for a 12 steps circle IMGUI_API void PathBezierCurveTo(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, int num_segments = 0); @@ -1760,15 +1922,20 @@ struct ImDrawData int TotalVtxCount; // For convenience, sum of all ImDrawList's VtxBuffer.Size ImVec2 DisplayPos; // Upper-left position of the viewport to render (== upper-left of the orthogonal projection matrix to use) ImVec2 DisplaySize; // Size of the viewport to render (== io.DisplaySize for the main viewport) (DisplayPos + DisplaySize == lower-right of the orthogonal projection matrix to use) + ImVec2 FramebufferScale; // Amount of pixels for each unit of DisplaySize. Based on io.DisplayFramebufferScale. Generally (1,1) on normal display, (2,2) on OSX with Retina display. // Functions ImDrawData() { Valid = false; Clear(); } ~ImDrawData() { Clear(); } - void Clear() { Valid = false; CmdLists = NULL; CmdListsCount = TotalVtxCount = TotalIdxCount = 0; DisplayPos = DisplaySize = ImVec2(0.f, 0.f); } // The ImDrawList are owned by ImGuiContext! - IMGUI_API void DeIndexAllBuffers(); // Helper to convert all buffers from indexed to non-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering! - IMGUI_API void ScaleClipRects(const ImVec2& sc); // Helper to scale the ClipRect field of each ImDrawCmd. Use if your final output buffer is at a different scale than ImGui expects, or if there is a difference between your window resolution and framebuffer resolution. + void Clear() { Valid = false; CmdLists = NULL; CmdListsCount = TotalVtxCount = TotalIdxCount = 0; DisplayPos = DisplaySize = FramebufferScale = ImVec2(0.f, 0.f); } // The ImDrawList are owned by ImGuiContext! + IMGUI_API void DeIndexAllBuffers(); // Helper to convert all buffers from indexed to non-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering! + IMGUI_API void ScaleClipRects(const ImVec2& fb_scale); // Helper to scale the ClipRect field of each ImDrawCmd. Use if your final output buffer is at a different scale than ImGui expects, or if there is a difference between your window resolution and framebuffer resolution. }; +//----------------------------------------------------------------------------- +// Font API (ImFontConfig, ImFontGlyph, ImFontAtlasFlags, ImFontAtlas, ImFontGlyphRangesBuilder, ImFont) +//----------------------------------------------------------------------------- + struct ImFontConfig { void* FontData; // // TTF/OTF data @@ -1776,8 +1943,8 @@ struct ImFontConfig bool FontDataOwnedByAtlas; // true // TTF/OTF data ownership taken by the container ImFontAtlas (will delete memory itself). int FontNo; // 0 // Index of font within TTF/OTF file float SizePixels; // // Size in pixels for rasterizer (more or less maps to the resulting font height). - int OversampleH; // 3 // Rasterize at higher quality for sub-pixel positioning. We don't use sub-pixel positions on the Y axis. - int OversampleV; // 1 // Rasterize at higher quality for sub-pixel positioning. We don't use sub-pixel positions on the Y axis. + int OversampleH; // 3 // Rasterize at higher quality for sub-pixel positioning. Read https://github.com/nothings/stb/blob/master/tests/oversample/README.md for details. + int OversampleV; // 1 // Rasterize at higher quality for sub-pixel positioning. We don't use sub-pixel positions on the Y axis. bool PixelSnapH; // false // Align every glyph to pixel boundary. Useful e.g. if you are merging a non-pixel aligned font with the default font. If enabled, you can set OversampleH/V to 1. ImVec2 GlyphExtraSpacing; // 0, 0 // Extra spacing (in pixels) between glyphs. Only X axis is supported for now. ImVec2 GlyphOffset; // 0, 0 // Offset all glyphs from this font input. @@ -1803,6 +1970,21 @@ struct ImFontGlyph float U0, V0, U1, V1; // Texture coordinates }; +// Helper to build glyph ranges from text/string data. Feed your application strings/characters to it then call BuildRanges(). +// This is essentially a tightly packed of vector of 64k booleans = 8KB storage. +struct ImFontGlyphRangesBuilder +{ + ImVector UsedChars; // Store 1-bit per Unicode code point (0=unused, 1=used) + + ImFontGlyphRangesBuilder() { UsedChars.resize(0x10000 / sizeof(int)); memset(UsedChars.Data, 0, 0x10000 / sizeof(int)); } + bool GetBit(int n) const { int off = (n >> 5); int mask = 1 << (n & 31); return (UsedChars[off] & mask) != 0; } // Get bit n in the array + void SetBit(int n) { int off = (n >> 5); int mask = 1 << (n & 31); UsedChars[off] |= mask; } // Set bit n in the array + void AddChar(ImWchar c) { SetBit(c); } // Add character + IMGUI_API void AddText(const char* text, const char* text_end = NULL); // Add string (each character of the UTF-8 string are added) + IMGUI_API void AddRanges(const ImWchar* ranges); // Add ranges, e.g. builder.AddRanges(ImFontAtlas::GetGlyphRangesDefault()) to force add all of ASCII/Latin+Ext + IMGUI_API void BuildRanges(ImVector* out_ranges); // Output new ranges +}; + enum ImFontAtlasFlags_ { ImFontAtlasFlags_None = 0, @@ -1818,13 +2000,13 @@ enum ImFontAtlasFlags_ // - Optionally, call any of the AddFont*** functions. If you don't call any, the default font embedded in the code will be loaded for you. // - Call GetTexDataAsAlpha8() or GetTexDataAsRGBA32() to build and retrieve pixels data. // - Upload the pixels data into a texture within your graphics system (see imgui_impl_xxxx.cpp examples) -// - Call SetTexID(my_tex_id); and pass the pointer/identifier to your texture in a format natural to your graphics API. +// - Call SetTexID(my_tex_id); and pass the pointer/identifier to your texture in a format natural to your graphics API. // This value will be passed back to you during rendering to identify the texture. Read FAQ entry about ImTextureID for more details. // Common pitfalls: -// - If you pass a 'glyph_ranges' array to AddFont*** functions, you need to make sure that your array persist up until the +// - If you pass a 'glyph_ranges' array to AddFont*** functions, you need to make sure that your array persist up until the // atlas is build (when calling GetTexData*** or Build()). We only copy the pointer, not the data. // - Important: By default, AddFontFromMemoryTTF() takes ownership of the data. Even though we are not writing to it, we will free the pointer on destruction. -// You can set font_cfg->FontDataOwnedByAtlas=false to keep ownership of your data and it won't be freed, +// You can set font_cfg->FontDataOwnedByAtlas=false to keep ownership of your data and it won't be freed, // - Even though many functions are suffixed with "TTF", OTF data is supported just as well. // - This is an old API and it is currently awkward for those and and various other reasons! We will address them in the future! struct ImFontAtlas @@ -1845,12 +2027,12 @@ struct ImFontAtlas // Build atlas, retrieve pixel data. // User is in charge of copying the pixels into graphics memory (e.g. create a texture with your engine). Then store your texture handle with SetTexID(). // The pitch is always = Width * BytesPerPixels (1 or 4) - // Building in RGBA32 format is provided for convenience and compatibility, but note that unless you manually manipulate or copy color data into + // Building in RGBA32 format is provided for convenience and compatibility, but note that unless you manually manipulate or copy color data into // the texture (e.g. when using the AddCustomRect*** api), then the RGB pixels emitted will always be white (~75% of memory/bandwidth waste. IMGUI_API bool Build(); // Build pixels data. This is called automatically for you by the GetTexData*** functions. - IMGUI_API bool IsBuilt() { return Fonts.Size > 0 && (TexPixelsAlpha8 != NULL || TexPixelsRGBA32 != NULL); } IMGUI_API void GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL); // 1 byte per-pixel IMGUI_API void GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL); // 4 bytes-per-pixel + bool IsBuilt() { return Fonts.Size > 0 && (TexPixelsAlpha8 != NULL || TexPixelsRGBA32 != NULL); } void SetTexID(ImTextureID id) { TexID = id; } //------------------------------------------- @@ -1859,7 +2041,7 @@ struct ImFontAtlas // Helpers to retrieve list of common Unicode ranges (2 value per range, values are inclusive, zero-terminated list) // NB: Make sure that your string are UTF-8 and NOT in your local code page. In C++11, you can create UTF-8 string literal using the u8"Hello world" syntax. See FAQ for details. - // NB: Consider using GlyphRangesBuilder to build glyph ranges from textual data. + // NB: Consider using ImFontGlyphRangesBuilder to build glyph ranges from textual data. IMGUI_API const ImWchar* GetGlyphRangesDefault(); // Basic Latin, Extended Latin IMGUI_API const ImWchar* GetGlyphRangesKorean(); // Default + Korean characters IMGUI_API const ImWchar* GetGlyphRangesJapanese(); // Default + Hiragana, Katakana, Half-Width, Selection of 1946 Ideographs @@ -1867,19 +2049,7 @@ struct ImFontAtlas IMGUI_API const ImWchar* GetGlyphRangesChineseSimplifiedCommon();// Default + Half-Width + Japanese Hiragana/Katakana + set of 2500 CJK Unified Ideographs for common simplified Chinese IMGUI_API const ImWchar* GetGlyphRangesCyrillic(); // Default + about 400 Cyrillic characters IMGUI_API const ImWchar* GetGlyphRangesThai(); // Default + Thai characters - - // Helpers to build glyph ranges from text data. Feed your application strings/characters to it then call BuildRanges(). - struct GlyphRangesBuilder - { - ImVector UsedChars; // Store 1-bit per Unicode code point (0=unused, 1=used) - GlyphRangesBuilder() { UsedChars.resize(0x10000 / 8); memset(UsedChars.Data, 0, 0x10000 / 8); } - bool GetBit(int n) const { return (UsedChars[n >> 3] & (1 << (n & 7))) != 0; } - void SetBit(int n) { UsedChars[n >> 3] |= (unsigned char) (1 << (n & 7)); } //Set bit 'c' in the array [Bruno Levy] added cast to silence warning - void AddChar(ImWchar c) { SetBit(c); } // Add character - IMGUI_API void AddText(const char* text, const char* text_end = NULL); // Add string (each character of the UTF-8 string are added) - IMGUI_API void AddRanges(const ImWchar* ranges); // Add ranges, e.g. builder.AddRanges(ImFontAtlas::GetGlyphRangesDefault()) to force add all of ASCII/Latin+Ext - IMGUI_API void BuildRanges(ImVector* out_ranges); // Output new ranges - }; + IMGUI_API const ImWchar* GetGlyphRangesVietnamese(); // Default + Vietname characters //------------------------------------------- // Custom Rectangles/Glyphs API @@ -1915,7 +2085,7 @@ struct ImFontAtlas ImFontAtlasFlags Flags; // Build flags (see ImFontAtlasFlags_) ImTextureID TexID; // User data to refer to the texture once it has been uploaded to user's graphic systems. It is passed back to you during rendering via the ImDrawCmd structure. int TexDesiredWidth; // Texture width desired by user before Build(). Must be a power-of-two. If have many glyphs your graphics API have texture size restrictions you may want to increase texture width to decrease height. - int TexGlyphPadding; // Padding between glyphs within texture in pixels. Defaults to 1. + int TexGlyphPadding; // Padding between glyphs within texture in pixels. Defaults to 1. If your rendering method doesn't rely on bilinear filtering you may set this to 0. // [Internal] // NB: Access texture data via GetTexData*() calls! Which will setup a default font for you. @@ -1929,39 +2099,42 @@ struct ImFontAtlas ImVector CustomRects; // Rectangles for packing custom texture data into the atlas. ImVector ConfigData; // Internal data int CustomRectIds[1]; // Identifiers of custom texture rectangle used by ImFontAtlas/ImDrawList + +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + typedef ImFontGlyphRangesBuilder GlyphRangesBuilder; // OBSOLETE 1.67+ +#endif }; // Font runtime data and rendering // ImFontAtlas automatically loads a default embedded font for you when you call GetTexDataAsAlpha8() or GetTexDataAsRGBA32(). struct ImFont { - // Members: Hot ~62/78 bytes - float FontSize; // // Height of characters, set during loading (don't change after loading) - float Scale; // = 1.f // Base font scale, multiplied by the per-window font scale which you can adjust with SetFontScale() - ImVec2 DisplayOffset; // = (0.f,0.f) // Offset font rendering by xx pixels - ImVector Glyphs; // // All glyphs. - ImVector IndexAdvanceX; // // Sparse. Glyphs->AdvanceX in a directly indexable way (more cache-friendly, for CalcTextSize functions which are often bottleneck in large UI). - ImVector IndexLookup; // // Sparse. Index glyphs by Unicode code-point. - const ImFontGlyph* FallbackGlyph; // == FindGlyph(FontFallbackChar) - float FallbackAdvanceX; // == FallbackGlyph->AdvanceX - ImWchar FallbackChar; // = '?' // Replacement glyph if one isn't found. Only set via SetFallbackChar() - - // Members: Cold ~18/26 bytes - short ConfigDataCount; // ~ 1 // Number of ImFontConfig involved in creating this font. Bigger than 1 when merging multiple font sources into one ImFont. - ImFontConfig* ConfigData; // // Pointer within ContainerAtlas->ConfigData - ImFontAtlas* ContainerAtlas; // // What we has been loaded into - float Ascent, Descent; // // Ascent: distance from top to bottom of e.g. 'A' [0..FontSize] - bool DirtyLookupTables; - int MetricsTotalSurface;// // Total surface in pixels to get an idea of the font rasterization/texture cost (not exact, we approximate the cost of padding between glyphs) + // Members: Hot ~20/24 bytes (for CalcTextSize) + ImVector IndexAdvanceX; // 12-16 // out // // Sparse. Glyphs->AdvanceX in a directly indexable way (cache-friendly for CalcTextSize functions which only this this info, and are often bottleneck in large UI). + float FallbackAdvanceX; // 4 // out // = FallbackGlyph->AdvanceX + float FontSize; // 4 // in // // Height of characters/line, set during loading (don't change after loading) + + // Members: Hot ~36/48 bytes (for CalcTextSize + render loop) + ImVector IndexLookup; // 12-16 // out // // Sparse. Index glyphs by Unicode code-point. + ImVector Glyphs; // 12-16 // out // // All glyphs. + const ImFontGlyph* FallbackGlyph; // 4-8 // out // = FindGlyph(FontFallbackChar) + ImVec2 DisplayOffset; // 8 // in // = (0,0) // Offset font rendering by xx pixels + + // Members: Cold ~32/40 bytes + ImFontAtlas* ContainerAtlas; // 4-8 // out // // What we has been loaded into + const ImFontConfig* ConfigData; // 4-8 // in // // Pointer within ContainerAtlas->ConfigData + short ConfigDataCount; // 2 // in // ~ 1 // Number of ImFontConfig involved in creating this font. Bigger than 1 when merging multiple font sources into one ImFont. + ImWchar FallbackChar; // 2 // in // = '?' // Replacement glyph if one isn't found. Only set via SetFallbackChar() + float Scale; // 4 // in // = 1.f // Base font scale, multiplied by the per-window font scale which you can adjust with SetWindowFontScale() + float Ascent, Descent; // 4+4 // out // // Ascent: distance from top to bottom of e.g. 'A' [0..FontSize] + int MetricsTotalSurface;// 4 // out // // Total surface in pixels to get an idea of the font rasterization/texture cost (not exact, we approximate the cost of padding between glyphs) + bool DirtyLookupTables; // 1 // out // // Methods IMGUI_API ImFont(); IMGUI_API ~ImFont(); - IMGUI_API void ClearOutputData(); - IMGUI_API void BuildLookupTable(); IMGUI_API const ImFontGlyph*FindGlyph(ImWchar c) const; IMGUI_API const ImFontGlyph*FindGlyphNoFallback(ImWchar c) const; - IMGUI_API void SetFallbackChar(ImWchar c); float GetCharAdvance(ImWchar c) const { return ((int)c < IndexAdvanceX.Size) ? IndexAdvanceX[(int)c] : FallbackAdvanceX; } bool IsLoaded() const { return ContainerAtlas != NULL; } const char* GetDebugName() const { return ConfigData ? ConfigData->Name : ""; } @@ -1973,10 +2146,13 @@ struct ImFont IMGUI_API void RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, ImWchar c) const; IMGUI_API void RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width = 0.0f, bool cpu_fine_clip = false) const; - // [Internal] + // [Internal] Don't use! + IMGUI_API void BuildLookupTable(); + IMGUI_API void ClearOutputData(); IMGUI_API void GrowIndex(int new_size); IMGUI_API void AddGlyph(ImWchar c, float x0, float y0, float x1, float y1, float u0, float v0, float u1, float v1, float advance_x); IMGUI_API void AddRemapChar(ImWchar dst, ImWchar src, bool overwrite_dst = true); // Makes 'dst' character/glyph points to 'src' character/glyph. Currently needs to be called AFTER fonts have been built. + IMGUI_API void SetFallbackChar(ImWchar c); #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS typedef ImFontGlyph Glyph; // OBSOLETE 1.52+ diff --git a/src/lib/geogram_gfx/third_party/ImGui/imgui_demo.cpp b/src/lib/geogram_gfx/third_party/ImGui/imgui_demo.cpp index b7f53104..b54475db 100644 --- a/src/lib/geogram_gfx/third_party/ImGui/imgui_demo.cpp +++ b/src/lib/geogram_gfx/third_party/ImGui/imgui_demo.cpp @@ -1,24 +1,24 @@ -// dear imgui, v1.66 +// dear imgui, v1.69 // (demo code) // Message to the person tempted to delete this file when integrating Dear ImGui into their code base: // Do NOT remove this file from your project! Think again! It is the most useful reference code that you and other coders -// will want to refer to and call. Have the ImGui::ShowDemoWindow() function wired in an always-available debug menu of -// your game/app! Removing this file from your project is hindering access to documentation for everyone in your team, +// will want to refer to and call. Have the ImGui::ShowDemoWindow() function wired in an always-available debug menu of +// your game/app! Removing this file from your project is hindering access to documentation for everyone in your team, // likely leading you to poorer usage of the library. // Everything in this file will be stripped out by the linker if you don't call ImGui::ShowDemoWindow(). -// If you want to link core Dear ImGui in your shipped builds but want an easy guarantee that the demo will not be linked, +// If you want to link core Dear ImGui in your shipped builds but want an easy guarantee that the demo will not be linked, // you can setup your imconfig.h with #define IMGUI_DISABLE_DEMO_WINDOWS and those functions will be empty. // In other situation, whenever you have Dear ImGui available you probably want this to be available for reference. // Thank you, // -Your beloved friend, imgui_demo.cpp (that you won't delete) -// Message to beginner C/C++ programmers about the meaning of the 'static' keyword: -// In this demo code, we frequently we use 'static' variables inside functions. A static variable persist across calls, so it is -// essentially like a global variable but declared inside the scope of the function. We do this as a way to gather code and data +// Message to beginner C/C++ programmers about the meaning of the 'static' keyword: +// In this demo code, we frequently we use 'static' variables inside functions. A static variable persist across calls, so it is +// essentially like a global variable but declared inside the scope of the function. We do this as a way to gather code and data // in the same place, to make the demo source code faster to read, faster to write, and smaller in size. // It also happens to be a convenient way of storing simple UI related information as long as your function doesn't need to be reentrant -// or used in threads. This might be a pattern you will want to use in your code, but most of the real data you would be editing is +// or used in threads. This might be a pattern you will want to use in your code, but most of the real data you would be editing is // likely going to be stored outside your functions. /* @@ -27,6 +27,7 @@ Index of this file: // [SECTION] Forward Declarations, Helpers // [SECTION] Demo Window / ShowDemoWindow() +// [SECTION] About Window / ShowAboutWindow() // [SECTION] Style Editor / ShowStyleEditor() // [SECTION] Example App: Main Menu Bar / ShowExampleAppMainMenuBar() // [SECTION] Example App: Debug Console / ShowExampleAppConsole() @@ -39,6 +40,7 @@ Index of this file: // [SECTION] Example App: Simple Overlay / ShowExampleAppSimpleOverlay() // [SECTION] Example App: Manipulating Window Titles / ShowExampleAppWindowTitles() // [SECTION] Example App: Custom Rendering using ImDrawList API / ShowExampleAppCustomRendering() +// [SECTION] Example App: Documents Handling / ShowExampleAppDocuments() */ @@ -60,7 +62,6 @@ Index of this file: #ifdef _MSC_VER #pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen -#define vsnprintf _vsnprintf #endif #ifdef __clang__ #pragma clang diagnostic ignored "-Wold-style-cast" // warning : use of old-style cast // yes, they are more terse. @@ -68,6 +69,13 @@ Index of this file: #pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning : cast to 'void *' from smaller integer type 'int' #pragma clang diagnostic ignored "-Wformat-security" // warning : warning: format string is not a string literal #pragma clang diagnostic ignored "-Wexit-time-destructors" // warning : declaration requires an exit-time destructor // exit-time destruction order is undefined. if MemFree() leads to users code that has been disabled before exit it might cause problems. ImGui coding style welcomes static/globals. +#pragma clang diagnostic ignored "-Wunused-macros" // warning : warning: macro is not used // we define snprintf/vsnprintf on Windows so they are available, but not always used. +#if __has_warning("-Wzero-as-null-pointer-constant") +#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning : zero as null pointer constant // some standard header variations use #define NULL 0 +#endif +#if __has_warning("-Wdouble-promotion") +#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double. +#endif #if __has_warning("-Wreserved-id-macro") #pragma clang diagnostic ignored "-Wreserved-id-macro" // warning : macro name is a reserved identifier // #endif @@ -83,9 +91,11 @@ Index of this file: // Play it nice with Windows users. Notepad in 2017 still doesn't display text data with Unix-style \n. #ifdef _WIN32 -#define IM_NEWLINE "\r\n" +#define IM_NEWLINE "\r\n" +#define snprintf _snprintf +#define vsnprintf _vsnprintf #else -#define IM_NEWLINE "\n" +#define IM_NEWLINE "\n" #endif #define IM_MAX(_A,_B) (((_A) >= (_B)) ? (_A) : (_B)) @@ -101,6 +111,7 @@ Index of this file: #if !defined(IMGUI_DISABLE_DEMO_WINDOWS) // Forward Declarations +static void ShowExampleAppDocuments(bool* p_open); static void ShowExampleAppMainMenuBar(); static void ShowExampleAppConsole(bool* p_open); static void ShowExampleAppLog(bool* p_open); @@ -115,7 +126,7 @@ static void ShowExampleAppCustomRendering(bool* p_open); static void ShowExampleMenuFile(); // Helper to display a little (?) mark which shows a tooltip when hovered. -static void ShowHelpMarker(const char* desc) +static void HelpMarker(const char* desc) { ImGui::TextDisabled("(?)"); if (ImGui::IsItemHovered()) @@ -167,6 +178,7 @@ static void ShowDemoWindowMisc(); void ImGui::ShowDemoWindow(bool* p_open) { // Examples Apps (accessible from the "Examples" menu) + static bool show_app_documents = false; static bool show_app_main_menu_bar = false; static bool show_app_console = false; static bool show_app_log = false; @@ -179,6 +191,7 @@ void ImGui::ShowDemoWindow(bool* p_open) static bool show_app_window_titles = false; static bool show_app_custom_rendering = false; + if (show_app_documents) ShowExampleAppDocuments(&show_app_documents); // Process the Document app next, as it may also use a DockSpace() if (show_app_main_menu_bar) ShowExampleAppMainMenuBar(); if (show_app_console) ShowExampleAppConsole(&show_app_console); if (show_app_log) ShowExampleAppLog(&show_app_log); @@ -198,15 +211,7 @@ void ImGui::ShowDemoWindow(bool* p_open) if (show_app_metrics) { ImGui::ShowMetricsWindow(&show_app_metrics); } if (show_app_style_editor) { ImGui::Begin("Style Editor", &show_app_style_editor); ImGui::ShowStyleEditor(); ImGui::End(); } - if (show_app_about) - { - ImGui::Begin("About Dear ImGui", &show_app_about, ImGuiWindowFlags_AlwaysAutoResize); - ImGui::Text("Dear ImGui, %s", ImGui::GetVersion()); - ImGui::Separator(); - ImGui::Text("By Omar Cornut and all dear imgui contributors."); - ImGui::Text("Dear ImGui is licensed under the MIT License, see LICENSE for more information."); - ImGui::End(); - } + if (show_app_about) { ImGui::ShowAboutWindow(&show_app_about); } // Demonstrate the various window flags. Typically you would just use the default! static bool no_titlebar = false; @@ -270,6 +275,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::MenuItem("Simple overlay", NULL, &show_app_simple_overlay); ImGui::MenuItem("Manipulating window titles", NULL, &show_app_window_titles); ImGui::MenuItem("Custom rendering", NULL, &show_app_custom_rendering); + ImGui::MenuItem("Documents", NULL, &show_app_documents); ImGui::EndMenu(); } if (ImGui::BeginMenu("Help")) @@ -305,9 +311,9 @@ void ImGui::ShowDemoWindow(bool* p_open) { ImGui::CheckboxFlags("io.ConfigFlags: NavEnableKeyboard", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NavEnableKeyboard); ImGui::CheckboxFlags("io.ConfigFlags: NavEnableGamepad", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NavEnableGamepad); - ImGui::SameLine(); ShowHelpMarker("Required back-end to feed in gamepad inputs in io.NavInputs[] and set io.BackendFlags |= ImGuiBackendFlags_HasGamepad.\n\nRead instructions in imgui.cpp for details."); + ImGui::SameLine(); HelpMarker("Required back-end to feed in gamepad inputs in io.NavInputs[] and set io.BackendFlags |= ImGuiBackendFlags_HasGamepad.\n\nRead instructions in imgui.cpp for details."); ImGui::CheckboxFlags("io.ConfigFlags: NavEnableSetMousePos", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NavEnableSetMousePos); - ImGui::SameLine(); ShowHelpMarker("Instruct navigation to move the mouse cursor. See comment for ImGuiConfigFlags_NavEnableSetMousePos."); + ImGui::SameLine(); HelpMarker("Instruct navigation to move the mouse cursor. See comment for ImGuiConfigFlags_NavEnableSetMousePos."); ImGui::CheckboxFlags("io.ConfigFlags: NoMouse", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NoMouse); if (io.ConfigFlags & ImGuiConfigFlags_NoMouse) // Create a way to restore this flag otherwise we could be stuck completely! { @@ -320,19 +326,21 @@ void ImGui::ShowDemoWindow(bool* p_open) io.ConfigFlags &= ~ImGuiConfigFlags_NoMouse; } ImGui::CheckboxFlags("io.ConfigFlags: NoMouseCursorChange", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NoMouseCursorChange); - ImGui::SameLine(); ShowHelpMarker("Instruct back-end to not alter mouse cursor shape and visibility."); + ImGui::SameLine(); HelpMarker("Instruct back-end to not alter mouse cursor shape and visibility."); ImGui::Checkbox("io.ConfigInputTextCursorBlink", &io.ConfigInputTextCursorBlink); - ImGui::SameLine(); ShowHelpMarker("Set to false to disable blinking cursor, for users who consider it distracting"); - ImGui::Checkbox("io.ConfigResizeWindowsFromEdges [beta]", &io.ConfigResizeWindowsFromEdges); - ImGui::SameLine(); ShowHelpMarker("Enable resizing of windows from their edges and from the lower-left corner.\nThis requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback."); + ImGui::SameLine(); HelpMarker("Set to false to disable blinking cursor, for users who consider it distracting"); + ImGui::Checkbox("io.ConfigWindowsResizeFromEdges", &io.ConfigWindowsResizeFromEdges); + ImGui::SameLine(); HelpMarker("Enable resizing of windows from their edges and from the lower-left corner.\nThis requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback."); + ImGui::Checkbox("io.ConfigWindowsMoveFromTitleBarOnly", &io.ConfigWindowsMoveFromTitleBarOnly); ImGui::Checkbox("io.MouseDrawCursor", &io.MouseDrawCursor); - ImGui::SameLine(); ShowHelpMarker("Instruct Dear ImGui to render a mouse cursor for you. Note that a mouse cursor rendered via your application GPU rendering path will feel more laggy than hardware cursor, but will be more in sync with your other visuals.\n\nSome desktop applications may use both kinds of cursors (e.g. enable software cursor only when resizing/dragging something)."); + ImGui::SameLine(); HelpMarker("Instruct Dear ImGui to render a mouse cursor for you. Note that a mouse cursor rendered via your application GPU rendering path will feel more laggy than hardware cursor, but will be more in sync with your other visuals.\n\nSome desktop applications may use both kinds of cursors (e.g. enable software cursor only when resizing/dragging something)."); ImGui::TreePop(); ImGui::Separator(); } if (ImGui::TreeNode("Backend Flags")) { + HelpMarker("Those flags are set by the back-ends (imgui_impl_xxx files) to specify their capabilities."); ImGuiBackendFlags backend_flags = io.BackendFlags; // Make a local copy to avoid modifying the back-end flags. ImGui::CheckboxFlags("io.BackendFlags: HasGamepad", (unsigned int *)&backend_flags, ImGuiBackendFlags_HasGamepad); ImGui::CheckboxFlags("io.BackendFlags: HasMouseCursors", (unsigned int *)&backend_flags, ImGuiBackendFlags_HasMouseCursors); @@ -351,7 +359,7 @@ void ImGui::ShowDemoWindow(bool* p_open) if (ImGui::TreeNode("Capture/Logging")) { ImGui::TextWrapped("The logging API redirects all text output so you can easily capture the content of a window or a block. Tree nodes can be automatically expanded."); - ShowHelpMarker("Try opening any of the contents below in this window and then click one of the \"Log To\" button."); + HelpMarker("Try opening any of the contents below in this window and then click one of the \"Log To\" button."); ImGui::LogButtons(); ImGui::TextWrapped("You can also call ImGui::LogText() to output directly to the log without a visual output."); if (ImGui::Button("Copy \"Hello, world!\" to clipboard")) @@ -416,7 +424,7 @@ static void ShowDemoWindowWidgets() // Color buttons, demonstrate using PushID() to add unique identifier in the ID stack, and changing style. for (int i = 0; i < 7; i++) { - if (i > 0) + if (i > 0) ImGui::SameLine(); ImGui::PushID(i); ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(i/7.0f, 0.6f, 0.6f)); @@ -468,27 +476,30 @@ static void ShowDemoWindowWidgets() const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLLLLL", "MMMM", "OOOOOOO" }; static int item_current = 0; ImGui::Combo("combo", &item_current, items, IM_ARRAYSIZE(items)); - ImGui::SameLine(); ShowHelpMarker("Refer to the \"Combo\" section below for an explanation of the full BeginCombo/EndCombo API, and demonstration of various flags.\n"); + ImGui::SameLine(); HelpMarker("Refer to the \"Combo\" section below for an explanation of the full BeginCombo/EndCombo API, and demonstration of various flags.\n"); } { static char str0[128] = "Hello, world!"; - static int i0 = 123; ImGui::InputText("input text", str0, IM_ARRAYSIZE(str0)); - ImGui::SameLine(); ShowHelpMarker("USER:\nHold SHIFT or use mouse to select text.\n" "CTRL+Left/Right to word jump.\n" "CTRL+A or double-click to select all.\n" "CTRL+X,CTRL+C,CTRL+V clipboard.\n" "CTRL+Z,CTRL+Y undo/redo.\n" "ESCAPE to revert.\n\nPROGRAMMER:\nYou can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputText() to a dynamic string type. See misc/cpp/imgui_stdlib.h for an example (this is not demonstrated in imgui_demo.cpp)."); + ImGui::SameLine(); HelpMarker("USER:\nHold SHIFT or use mouse to select text.\n" "CTRL+Left/Right to word jump.\n" "CTRL+A or double-click to select all.\n" "CTRL+X,CTRL+C,CTRL+V clipboard.\n" "CTRL+Z,CTRL+Y undo/redo.\n" "ESCAPE to revert.\n\nPROGRAMMER:\nYou can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputText() to a dynamic string type. See misc/cpp/imgui_stdlib.h for an example (this is not demonstrated in imgui_demo.cpp)."); + static char str1[128] = ""; + ImGui::InputTextWithHint("input text (w/ hint)", "enter text here", str1, IM_ARRAYSIZE(str1)); + + static int i0 = 123; ImGui::InputInt("input int", &i0); - ImGui::SameLine(); ShowHelpMarker("You can apply arithmetic operators +,*,/ on numerical values.\n e.g. [ 100 ], input \'*2\', result becomes [ 200 ]\nUse +- to subtract.\n"); + ImGui::SameLine(); HelpMarker("You can apply arithmetic operators +,*,/ on numerical values.\n e.g. [ 100 ], input \'*2\', result becomes [ 200 ]\nUse +- to subtract.\n"); static float f0 = 0.001f; - ImGui::InputFloat("input float", &f0, 0.01f, 1.0f); + ImGui::InputFloat("input float", &f0, 0.01f, 1.0f, "%.3f"); static double d0 = 999999.00000001; ImGui::InputDouble("input double", &d0, 0.01f, 1.0f, "%.8f"); static float f1 = 1.e10f; ImGui::InputFloat("input scientific", &f1, 0.0f, 0.0f, "%e"); - ImGui::SameLine(); ShowHelpMarker("You can input value using the scientific notation,\n e.g. \"1e+8\" becomes \"100000000\".\n"); + ImGui::SameLine(); HelpMarker("You can input value using the scientific notation,\n e.g. \"1e+8\" becomes \"100000000\".\n"); static float vec4a[4] = { 0.10f, 0.20f, 0.30f, 0.44f }; ImGui::InputFloat3("input float3", vec4a); @@ -497,7 +508,7 @@ static void ShowDemoWindowWidgets() { static int i1 = 50, i2 = 42; ImGui::DragInt("drag int", &i1, 1); - ImGui::SameLine(); ShowHelpMarker("Click and drag to edit value.\nHold SHIFT/ALT for faster/slower edit.\nDouble-click or CTRL+click to input value."); + ImGui::SameLine(); HelpMarker("Click and drag to edit value.\nHold SHIFT/ALT for faster/slower edit.\nDouble-click or CTRL+click to input value."); ImGui::DragInt("drag int 0..100", &i2, 1, 0, 100, "%d%%"); @@ -509,7 +520,7 @@ static void ShowDemoWindowWidgets() { static int i1=0; ImGui::SliderInt("slider int", &i1, -1, 3); - ImGui::SameLine(); ShowHelpMarker("CTRL+click to input value."); + ImGui::SameLine(); HelpMarker("CTRL+click to input value."); static float f1=0.123f, f2=0.0f; ImGui::SliderFloat("slider float", &f1, 0.0f, 1.0f, "ratio = %.3f"); @@ -522,7 +533,7 @@ static void ShowDemoWindowWidgets() static float col1[3] = { 1.0f,0.0f,0.2f }; static float col2[4] = { 0.4f,0.7f,0.0f,0.5f }; ImGui::ColorEdit3("color 1", col1); - ImGui::SameLine(); ShowHelpMarker("Click on the colored square to open a color picker.\nClick and hold to use drag and drop.\nRight-click on the colored square to show options.\nCTRL+click on individual component to input value.\n"); + ImGui::SameLine(); HelpMarker("Click on the colored square to open a color picker.\nClick and hold to use drag and drop.\nRight-click on the colored square to show options.\nCTRL+click on individual component to input value.\n"); ImGui::ColorEdit4("color 2", col2); } @@ -565,7 +576,7 @@ static void ShowDemoWindowWidgets() if (ImGui::TreeNode("Advanced, with Selectable nodes")) { - ShowHelpMarker("This is a more standard looking tree with selectable nodes.\nClick to select, CTRL+Click to toggle, click on arrows or double-click to open."); + HelpMarker("This is a more standard looking tree with selectable nodes.\nClick to select, CTRL+Click to toggle, click on arrows or double-click to open."); static bool align_label_with_current_x_position = false; ImGui::Checkbox("Align label with current X position)", &align_label_with_current_x_position); ImGui::Text("Hello!"); @@ -652,7 +663,7 @@ static void ShowDemoWindowWidgets() ImGui::TextColored(ImVec4(1.0f,0.0f,1.0f,1.0f), "Pink"); ImGui::TextColored(ImVec4(1.0f,1.0f,0.0f,1.0f), "Yellow"); ImGui::TextDisabled("Disabled"); - ImGui::SameLine(); ShowHelpMarker("The TextDisabled color is stored in ImGuiStyle."); + ImGui::SameLine(); HelpMarker("The TextDisabled color is stored in ImGuiStyle."); ImGui::TreePop(); } @@ -759,6 +770,7 @@ static void ShowDemoWindowWidgets() // Expose flags as checkbox for the demo static ImGuiComboFlags flags = 0; ImGui::CheckboxFlags("ImGuiComboFlags_PopupAlignLeft", (unsigned int*)&flags, ImGuiComboFlags_PopupAlignLeft); + ImGui::SameLine(); HelpMarker("Only makes a difference if the popup is larger than the combo"); if (ImGui::CheckboxFlags("ImGuiComboFlags_NoArrowButton", (unsigned int*)&flags, ImGuiComboFlags_NoArrowButton)) flags &= ~ImGuiComboFlags_NoPreview; // Clear the other flag, as we cannot combine both if (ImGui::CheckboxFlags("ImGuiComboFlags_NoPreview", (unsigned int*)&flags, ImGuiComboFlags_NoPreview)) @@ -829,7 +841,7 @@ static void ShowDemoWindowWidgets() } if (ImGui::TreeNode("Selection State: Multiple Selection")) { - ShowHelpMarker("Hold CTRL and click to select multiple items."); + HelpMarker("Hold CTRL and click to select multiple items."); static bool selection[5] = { false, false, false, false, false }; for (int n = 0; n < 5; n++) { @@ -868,64 +880,130 @@ static void ShowDemoWindowWidgets() } if (ImGui::TreeNode("Grid")) { - static bool selected[16] = { true, false, false, false, false, true, false, false, false, false, true, false, false, false, false, true }; - for (int i = 0; i < 16; i++) + static bool selected[4*4] = { true, false, false, false, false, true, false, false, false, false, true, false, false, false, false, true }; + for (int i = 0; i < 4*4; i++) { ImGui::PushID(i); if (ImGui::Selectable("Sailor", &selected[i], 0, ImVec2(50,50))) { - int x = i % 4, y = i / 4; - if (x > 0) selected[i - 1] ^= 1; - if (x < 3) selected[i + 1] ^= 1; - if (y > 0) selected[i - 4] ^= 1; - if (y < 3) selected[i + 4] ^= 1; + // Note: We _unnecessarily_ test for both x/y and i here only to silence some static analyzer. The second part of each test is unnecessary. + int x = i % 4; + int y = i / 4; + if (x > 0) { selected[i - 1] ^= 1; } + if (x < 3 && i < 15) { selected[i + 1] ^= 1; } + if (y > 0 && i > 3) { selected[i - 4] ^= 1; } + if (y < 3 && i < 12) { selected[i + 4] ^= 1; } } if ((i % 4) < 3) ImGui::SameLine(); ImGui::PopID(); } ImGui::TreePop(); } + if (ImGui::TreeNode("Alignment")) + { + HelpMarker("Alignment applies when a selectable is larger than its text content.\nBy default, Selectables uses style.SelectableTextAlign but it can be overriden on a per-item basis using PushStyleVar()."); + static bool selected[3*3] = { true, false, true, false, true, false, true, false, true }; + for (int y = 0; y < 3; y++) + { + for (int x = 0; x < 3; x++) + { + ImVec2 alignment = ImVec2((float)x / 2.0f, (float)y / 2.0f); + char name[32]; + sprintf(name, "(%.1f,%.1f)", alignment.x, alignment.y); + if (x > 0) ImGui::SameLine(); + ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, alignment); + ImGui::Selectable(name, &selected[3*y+x], ImGuiSelectableFlags_None, ImVec2(80,80)); + ImGui::PopStyleVar(); + } + } + ImGui::TreePop(); + } ImGui::TreePop(); } - if (ImGui::TreeNode("Filtered Text Input")) + if (ImGui::TreeNode("Text Input")) { - static char buf1[64] = ""; ImGui::InputText("default", buf1, 64); - static char buf2[64] = ""; ImGui::InputText("decimal", buf2, 64, ImGuiInputTextFlags_CharsDecimal); - static char buf3[64] = ""; ImGui::InputText("hexadecimal", buf3, 64, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase); - static char buf4[64] = ""; ImGui::InputText("uppercase", buf4, 64, ImGuiInputTextFlags_CharsUppercase); - static char buf5[64] = ""; ImGui::InputText("no blank", buf5, 64, ImGuiInputTextFlags_CharsNoBlank); - struct TextFilters { static int FilterImGuiLetters(ImGuiInputTextCallbackData* data) { if (data->EventChar < 256 && strchr("imgui", (char)data->EventChar)) return 0; return 1; } }; - static char buf6[64] = ""; ImGui::InputText("\"imgui\" letters", buf6, 64, ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterImGuiLetters); + if (ImGui::TreeNode("Multi-line Text Input")) + { + // Note: we are using a fixed-sized buffer for simplicity here. See ImGuiInputTextFlags_CallbackResize + // and the code in misc/cpp/imgui_stdlib.h for how to setup InputText() for dynamically resizing strings. + static char text[1024 * 16] = + "/*\n" + " The Pentium F00F bug, shorthand for F0 0F C7 C8,\n" + " the hexadecimal encoding of one offending instruction,\n" + " more formally, the invalid operand with locked CMPXCHG8B\n" + " instruction bug, is a design flaw in the majority of\n" + " Intel Pentium, Pentium MMX, and Pentium OverDrive\n" + " processors (all in the P5 microarchitecture).\n" + "*/\n\n" + "label:\n" + "\tlock cmpxchg8b eax\n"; + + static ImGuiInputTextFlags flags = ImGuiInputTextFlags_AllowTabInput; + HelpMarker("You can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputTextMultiline() to a dynamic string type. See misc/cpp/imgui_stdlib.h for an example. (This is not demonstrated in imgui_demo.cpp)"); + ImGui::CheckboxFlags("ImGuiInputTextFlags_ReadOnly", (unsigned int*)&flags, ImGuiInputTextFlags_ReadOnly); + ImGui::CheckboxFlags("ImGuiInputTextFlags_AllowTabInput", (unsigned int*)&flags, ImGuiInputTextFlags_AllowTabInput); + ImGui::CheckboxFlags("ImGuiInputTextFlags_CtrlEnterForNewLine", (unsigned int*)&flags, ImGuiInputTextFlags_CtrlEnterForNewLine); + ImGui::InputTextMultiline("##source", text, IM_ARRAYSIZE(text), ImVec2(-1.0f, ImGui::GetTextLineHeight() * 16), flags); + ImGui::TreePop(); + } - ImGui::Text("Password input"); - static char bufpass[64] = "password123"; - ImGui::InputText("password", bufpass, 64, ImGuiInputTextFlags_Password | ImGuiInputTextFlags_CharsNoBlank); - ImGui::SameLine(); ShowHelpMarker("Display all characters as '*'.\nDisable clipboard cut and copy.\nDisable logging.\n"); - ImGui::InputText("password (clear)", bufpass, 64, ImGuiInputTextFlags_CharsNoBlank); + if (ImGui::TreeNode("Filtered Text Input")) + { + static char buf1[64] = ""; ImGui::InputText("default", buf1, 64); + static char buf2[64] = ""; ImGui::InputText("decimal", buf2, 64, ImGuiInputTextFlags_CharsDecimal); + static char buf3[64] = ""; ImGui::InputText("hexadecimal", buf3, 64, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase); + static char buf4[64] = ""; ImGui::InputText("uppercase", buf4, 64, ImGuiInputTextFlags_CharsUppercase); + static char buf5[64] = ""; ImGui::InputText("no blank", buf5, 64, ImGuiInputTextFlags_CharsNoBlank); + struct TextFilters { static int FilterImGuiLetters(ImGuiInputTextCallbackData* data) { if (data->EventChar < 256 && strchr("imgui", (char)data->EventChar)) return 0; return 1; } }; + static char buf6[64] = ""; ImGui::InputText("\"imgui\" letters", buf6, 64, ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterImGuiLetters); + + ImGui::Text("Password input"); + static char bufpass[64] = "password123"; + ImGui::InputText("password", bufpass, 64, ImGuiInputTextFlags_Password | ImGuiInputTextFlags_CharsNoBlank); + ImGui::SameLine(); HelpMarker("Display all characters as '*'.\nDisable clipboard cut and copy.\nDisable logging.\n"); + ImGui::InputTextWithHint("password (w/ hint)", "", bufpass, 64, ImGuiInputTextFlags_Password | ImGuiInputTextFlags_CharsNoBlank); + ImGui::InputText("password (clear)", bufpass, 64, ImGuiInputTextFlags_CharsNoBlank); + ImGui::TreePop(); + } - ImGui::TreePop(); - } + if (ImGui::TreeNode("Resize Callback")) + { + // If you have a custom string type you would typically create a ImGui::InputText() wrapper than takes your type as input. + // See misc/cpp/imgui_stdlib.h and .cpp for an implementation of this using std::string. + HelpMarker("Demonstrate using ImGuiInputTextFlags_CallbackResize to wire your resizable string type to InputText().\n\nSee misc/cpp/imgui_stdlib.h for an implementation of this for std::string."); + struct Funcs + { + static int MyResizeCallback(ImGuiInputTextCallbackData* data) + { + if (data->EventFlag == ImGuiInputTextFlags_CallbackResize) + { + ImVector* my_str = (ImVector*)data->UserData; + IM_ASSERT(my_str->begin() == data->Buf); + my_str->resize(data->BufSize); // NB: On resizing calls, generally data->BufSize == data->BufTextLen + 1 + data->Buf = my_str->begin(); + } + return 0; + } + + // Tip: Because ImGui:: is a namespace you can add your own function into the namespace from your own source files. + static bool MyInputTextMultiline(const char* label, ImVector* my_str, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0) + { + IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0); + return ImGui::InputTextMultiline(label, my_str->begin(), (size_t)my_str->size(), size, flags | ImGuiInputTextFlags_CallbackResize, Funcs::MyResizeCallback, (void*)my_str); + } + }; + + // For this demo we are using ImVector as a string container. + // Note that because we need to store a terminating zero character, our size/capacity are 1 more than usually reported by a typical string class. + static ImVector my_str; + if (my_str.empty()) + my_str.push_back(0); + Funcs::MyInputTextMultiline("##MyStr", &my_str, ImVec2(-1.0f, ImGui::GetTextLineHeight() * 16)); + ImGui::Text("Data: %p\nSize: %d\nCapacity: %d", (void*)my_str.begin(), my_str.size(), my_str.capacity()); + ImGui::TreePop(); + } - if (ImGui::TreeNode("Multi-line Text Input")) - { - static bool read_only = false; - static char text[1024*16] = - "/*\n" - " The Pentium F00F bug, shorthand for F0 0F C7 C8,\n" - " the hexadecimal encoding of one offending instruction,\n" - " more formally, the invalid operand with locked CMPXCHG8B\n" - " instruction bug, is a design flaw in the majority of\n" - " Intel Pentium, Pentium MMX, and Pentium OverDrive\n" - " processors (all in the P5 microarchitecture).\n" - "*/\n\n" - "label:\n" - "\tlock cmpxchg8b eax\n"; - - ShowHelpMarker("You can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputTextMultiline() to a dynamic string type. See misc/cpp/imgui_stdlib.h for an example. (This is not demonstrated in imgui_demo.cpp)"); - ImGui::Checkbox("Read-only", &read_only); - ImGuiInputTextFlags flags = ImGuiInputTextFlags_AllowTabInput | (read_only ? ImGuiInputTextFlags_ReadOnly : 0); - ImGui::InputTextMultiline("##source", text, IM_ARRAYSIZE(text), ImVec2(-1.0f, ImGui::GetTextLineHeight() * 16), flags); ImGui::TreePop(); } @@ -942,7 +1020,7 @@ static void ShowDemoWindowWidgets() static float values[90] = { 0 }; static int values_offset = 0; static double refresh_time = 0.0; - if (!animate || refresh_time == 0.0f) + if (!animate || refresh_time == 0.0) refresh_time = ImGui::GetTime(); while (refresh_time < ImGui::GetTime()) // Create dummy data at fixed 60 hz rate for the demo { @@ -995,7 +1073,7 @@ static void ShowDemoWindowWidgets() if (ImGui::TreeNode("Color/Picker Widgets")) { - static ImVec4 color = ImColor(114, 144, 154, 200); + static ImVec4 color = ImVec4(114.0f/255.0f, 144.0f/255.0f, 154.0f/255.0f, 200.0f/255.0f); static bool alpha_preview = true; static bool alpha_half_preview = false; @@ -1005,36 +1083,38 @@ static void ShowDemoWindowWidgets() ImGui::Checkbox("With Alpha Preview", &alpha_preview); ImGui::Checkbox("With Half Alpha Preview", &alpha_half_preview); ImGui::Checkbox("With Drag and Drop", &drag_and_drop); - ImGui::Checkbox("With Options Menu", &options_menu); ImGui::SameLine(); ShowHelpMarker("Right-click on the individual color widget to show options."); - ImGui::Checkbox("With HDR", &hdr); ImGui::SameLine(); ShowHelpMarker("Currently all this does is to lift the 0..1 limits on dragging widgets."); + ImGui::Checkbox("With Options Menu", &options_menu); ImGui::SameLine(); HelpMarker("Right-click on the individual color widget to show options."); + ImGui::Checkbox("With HDR", &hdr); ImGui::SameLine(); HelpMarker("Currently all this does is to lift the 0..1 limits on dragging widgets."); int misc_flags = (hdr ? ImGuiColorEditFlags_HDR : 0) | (drag_and_drop ? 0 : ImGuiColorEditFlags_NoDragDrop) | (alpha_half_preview ? ImGuiColorEditFlags_AlphaPreviewHalf : (alpha_preview ? ImGuiColorEditFlags_AlphaPreview : 0)) | (options_menu ? 0 : ImGuiColorEditFlags_NoOptions); ImGui::Text("Color widget:"); - ImGui::SameLine(); ShowHelpMarker("Click on the colored square to open a color picker.\nCTRL+click on individual component to input value.\n"); + ImGui::SameLine(); HelpMarker("Click on the colored square to open a color picker.\nCTRL+click on individual component to input value.\n"); ImGui::ColorEdit3("MyColor##1", (float*)&color, misc_flags); ImGui::Text("Color widget HSV with Alpha:"); - ImGui::ColorEdit4("MyColor##2", (float*)&color, ImGuiColorEditFlags_HSV | misc_flags); + ImGui::ColorEdit4("MyColor##2", (float*)&color, ImGuiColorEditFlags_DisplayHSV | misc_flags); ImGui::Text("Color widget with Float Display:"); ImGui::ColorEdit4("MyColor##2f", (float*)&color, ImGuiColorEditFlags_Float | misc_flags); ImGui::Text("Color button with Picker:"); - ImGui::SameLine(); ShowHelpMarker("With the ImGuiColorEditFlags_NoInputs flag you can hide all the slider/text inputs.\nWith the ImGuiColorEditFlags_NoLabel flag you can pass a non-empty label which will only be used for the tooltip and picker popup."); + ImGui::SameLine(); HelpMarker("With the ImGuiColorEditFlags_NoInputs flag you can hide all the slider/text inputs.\nWith the ImGuiColorEditFlags_NoLabel flag you can pass a non-empty label which will only be used for the tooltip and picker popup."); ImGui::ColorEdit4("MyColor##3", (float*)&color, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel | misc_flags); ImGui::Text("Color button with Custom Picker Popup:"); - // Generate a dummy palette - static bool saved_palette_inited = false; - static ImVec4 saved_palette[32]; - if (!saved_palette_inited) + // Generate a dummy default palette. The palette will persist and can be edited. + static bool saved_palette_init = true; + static ImVec4 saved_palette[32] = { }; + if (saved_palette_init) + { for (int n = 0; n < IM_ARRAYSIZE(saved_palette); n++) { ImGui::ColorConvertHSVtoRGB(n / 31.0f, 0.8f, 0.8f, saved_palette[n].x, saved_palette[n].y, saved_palette[n].z); saved_palette[n].w = 1.0f; // Alpha } - saved_palette_inited = true; + saved_palette_init = false; + } static ImVec4 backup_color; bool open_popup = ImGui::ColorButton("MyColor##3b", color, misc_flags); @@ -1047,12 +1127,12 @@ static void ShowDemoWindowWidgets() } if (ImGui::BeginPopup("mypicker")) { - // FIXME: Adding a drag and drop example here would be perfect! ImGui::Text("MY CUSTOM COLOR PICKER WITH AN AMAZING PALETTE!"); ImGui::Separator(); ImGui::ColorPicker4("##picker", (float*)&color, misc_flags | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoSmallPreview); ImGui::SameLine(); - ImGui::BeginGroup(); + + ImGui::BeginGroup(); // Lock X position ImGui::Text("Current"); ImGui::ColorButton("##current", color, ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf, ImVec2(60,40)); ImGui::Text("Previous"); @@ -1068,6 +1148,8 @@ static void ShowDemoWindowWidgets() if (ImGui::ColorButton("##palette", saved_palette[n], ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_NoTooltip, ImVec2(20,20))) color = ImVec4(saved_palette[n].x, saved_palette[n].y, saved_palette[n].z, color.w); // Preserve alpha! + // Allow user to drop colors into each palette entry + // (Note that ColorButton is already a drag source by default, unless using ImGuiColorEditFlags_NoDragDrop) if (ImGui::BeginDragDropTarget()) { if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F)) @@ -1092,7 +1174,7 @@ static void ShowDemoWindowWidgets() static bool side_preview = true; static bool ref_color = false; static ImVec4 ref_color_v(1.0f,0.0f,1.0f,0.5f); - static int inputs_mode = 2; + static int display_mode = 0; static int picker_mode = 0; ImGui::Checkbox("With Alpha", &alpha); ImGui::Checkbox("With Alpha Bar", &alpha_bar); @@ -1107,28 +1189,39 @@ static void ShowDemoWindowWidgets() ImGui::ColorEdit4("##RefColor", &ref_color_v.x, ImGuiColorEditFlags_NoInputs | misc_flags); } } - ImGui::Combo("Inputs Mode", &inputs_mode, "All Inputs\0No Inputs\0RGB Input\0HSV Input\0HEX Input\0"); + ImGui::Combo("Display Mode", &display_mode, "Auto/Current\0None\0RGB Only\0HSV Only\0Hex Only\0"); + ImGui::SameLine(); HelpMarker("ColorEdit defaults to displaying RGB inputs if you don't specify a display mode, but the user can change it with a right-click.\n\nColorPicker defaults to displaying RGB+HSV+Hex if you don't specify a display mode.\n\nYou can change the defaults using SetColorEditOptions()."); ImGui::Combo("Picker Mode", &picker_mode, "Auto/Current\0Hue bar + SV rect\0Hue wheel + SV triangle\0"); - ImGui::SameLine(); ShowHelpMarker("User can right-click the picker to change mode."); + ImGui::SameLine(); HelpMarker("User can right-click the picker to change mode."); ImGuiColorEditFlags flags = misc_flags; - if (!alpha) flags |= ImGuiColorEditFlags_NoAlpha; // This is by default if you call ColorPicker3() instead of ColorPicker4() - if (alpha_bar) flags |= ImGuiColorEditFlags_AlphaBar; - if (!side_preview) flags |= ImGuiColorEditFlags_NoSidePreview; - if (picker_mode == 1) flags |= ImGuiColorEditFlags_PickerHueBar; - if (picker_mode == 2) flags |= ImGuiColorEditFlags_PickerHueWheel; - if (inputs_mode == 1) flags |= ImGuiColorEditFlags_NoInputs; - if (inputs_mode == 2) flags |= ImGuiColorEditFlags_RGB; - if (inputs_mode == 3) flags |= ImGuiColorEditFlags_HSV; - if (inputs_mode == 4) flags |= ImGuiColorEditFlags_HEX; + if (!alpha) flags |= ImGuiColorEditFlags_NoAlpha; // This is by default if you call ColorPicker3() instead of ColorPicker4() + if (alpha_bar) flags |= ImGuiColorEditFlags_AlphaBar; + if (!side_preview) flags |= ImGuiColorEditFlags_NoSidePreview; + if (picker_mode == 1) flags |= ImGuiColorEditFlags_PickerHueBar; + if (picker_mode == 2) flags |= ImGuiColorEditFlags_PickerHueWheel; + if (display_mode == 1) flags |= ImGuiColorEditFlags_NoInputs; // Disable all RGB/HSV/Hex displays + if (display_mode == 2) flags |= ImGuiColorEditFlags_DisplayRGB; // Override display mode + if (display_mode == 3) flags |= ImGuiColorEditFlags_DisplayHSV; + if (display_mode == 4) flags |= ImGuiColorEditFlags_DisplayHex; ImGui::ColorPicker4("MyColor##4", (float*)&color, flags, ref_color ? &ref_color_v.x : NULL); ImGui::Text("Programmatically set defaults:"); - ImGui::SameLine(); ShowHelpMarker("SetColorEditOptions() is designed to allow you to set boot-time default.\nWe don't have Push/Pop functions because you can force options on a per-widget basis if needed, and the user can change non-forced ones with the options menu.\nWe don't have a getter to avoid encouraging you to persistently save values that aren't forward-compatible."); + ImGui::SameLine(); HelpMarker("SetColorEditOptions() is designed to allow you to set boot-time default.\nWe don't have Push/Pop functions because you can force options on a per-widget basis if needed, and the user can change non-forced ones with the options menu.\nWe don't have a getter to avoid encouraging you to persistently save values that aren't forward-compatible."); if (ImGui::Button("Default: Uint8 + HSV + Hue Bar")) - ImGui::SetColorEditOptions(ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_HSV | ImGuiColorEditFlags_PickerHueBar); + ImGui::SetColorEditOptions(ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_PickerHueBar); if (ImGui::Button("Default: Float + HDR + Hue Wheel")) ImGui::SetColorEditOptions(ImGuiColorEditFlags_Float | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_PickerHueWheel); + // HSV encoded support (to avoid RGB<>HSV round trips and singularities when S==0 or V==0) + static ImVec4 color_stored_as_hsv(0.23f, 1.0f, 1.0f, 1.0f); + ImGui::Spacing(); + ImGui::Text("HSV encoded colors"); + ImGui::SameLine(); HelpMarker("By default, colors are given to ColorEdit and ColorPicker in RGB, but ImGuiColorEditFlags_InputHSV allows you to store colors as HSV and pass them to ColorEdit and ColorPicker as HSV. This comes with the added benefit that you can manipulate hue values with the picker even when saturation or value are zero."); + ImGui::Text("Color widget with InputHSV:"); + ImGui::ColorEdit4("HSV shown as HSV##1", (float*)&color_stored_as_hsv, ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_InputHSV | ImGuiColorEditFlags_Float); + ImGui::ColorEdit4("HSV shown as RGB##1", (float*)&color_stored_as_hsv, ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_InputHSV | ImGuiColorEditFlags_Float); + ImGui::DragFloat4("Raw HSV values", (float*)&color_stored_as_hsv, 0.01f, 0.0f, 1.0f); + ImGui::TreePop(); } @@ -1144,15 +1237,15 @@ static void ShowDemoWindowWidgets() if (ImGui::TreeNode("Data Types")) { // The DragScalar/InputScalar/SliderScalar functions allow various data types: signed/unsigned int/long long and float/double - // To avoid polluting the public API with all possible combinations, we use the ImGuiDataType enum to pass the type, - // and passing all arguments by address. + // To avoid polluting the public API with all possible combinations, we use the ImGuiDataType enum to pass the type, + // and passing all arguments by address. // This is the reason the test code below creates local variables to hold "zero" "one" etc. for each types. - // In practice, if you frequently use a given type that is not covered by the normal API entry points, you can wrap it - // yourself inside a 1 line function which can take typed argument as value instead of void*, and then pass their address + // In practice, if you frequently use a given type that is not covered by the normal API entry points, you can wrap it + // yourself inside a 1 line function which can take typed argument as value instead of void*, and then pass their address // to the generic function. For example: - // bool MySliderU64(const char *label, u64* value, u64 min = 0, u64 max = 0, const char* format = "%lld") - // { - // return SliderScalar(label, ImGuiDataType_U64, value, &min, &max, format); + // bool MySliderU64(const char *label, u64* value, u64 min = 0, u64 max = 0, const char* format = "%lld") + // { + // return SliderScalar(label, ImGuiDataType_U64, value, &min, &max, format); // } // Limits (as helper variables that we can take the address of) @@ -1162,6 +1255,10 @@ static void ShowDemoWindowWidgets() ImS64 LLONG_MAX = 9223372036854775807LL; ImU64 ULLONG_MAX = (2ULL * 9223372036854775807LL + 1); #endif + const char s8_zero = 0, s8_one = 1, s8_fifty = 50, s8_min = -128, s8_max = 127; + const ImU8 u8_zero = 0, u8_one = 1, u8_fifty = 50, u8_min = 0, u8_max = 255; + const short s16_zero = 0, s16_one = 1, s16_fifty = 50, s16_min = -32768, s16_max = 32767; + const ImU16 u16_zero = 0, u16_one = 1, u16_fifty = 50, u16_min = 0, u16_max = 65535; const ImS32 s32_zero = 0, s32_one = 1, s32_fifty = 50, s32_min = INT_MIN/2, s32_max = INT_MAX/2, s32_hi_a = INT_MAX/2 - 100, s32_hi_b = INT_MAX/2; const ImU32 u32_zero = 0, u32_one = 1, u32_fifty = 50, u32_min = 0, u32_max = UINT_MAX/2, u32_hi_a = UINT_MAX/2 - 100, u32_hi_b = UINT_MAX/2; const ImS64 s64_zero = 0, s64_one = 1, s64_fifty = 50, s64_min = LLONG_MIN/2, s64_max = LLONG_MAX/2, s64_hi_a = LLONG_MAX/2 - 100, s64_hi_b = LLONG_MAX/2; @@ -1170,6 +1267,10 @@ static void ShowDemoWindowWidgets() const double f64_zero = 0., f64_one = 1., f64_lo_a = -1000000000000000.0, f64_hi_a = +1000000000000000.0; // State + static char s8_v = 127; + static ImU8 u8_v = 255; + static short s16_v = 32767; + static ImU16 u16_v = 65535; static ImS32 s32_v = -1; static ImU32 u32_v = (ImU32)-1; static ImS64 s64_v = -1; @@ -1180,17 +1281,25 @@ static void ShowDemoWindowWidgets() const float drag_speed = 0.2f; static bool drag_clamp = false; ImGui::Text("Drags:"); - ImGui::Checkbox("Clamp integers to 0..50", &drag_clamp); ImGui::SameLine(); ShowHelpMarker("As with every widgets in dear imgui, we never modify values unless there is a user interaction.\nYou can override the clamping limits by using CTRL+Click to input a value."); + ImGui::Checkbox("Clamp integers to 0..50", &drag_clamp); ImGui::SameLine(); HelpMarker("As with every widgets in dear imgui, we never modify values unless there is a user interaction.\nYou can override the clamping limits by using CTRL+Click to input a value."); + ImGui::DragScalar("drag s8", ImGuiDataType_S8, &s8_v, drag_speed, drag_clamp ? &s8_zero : NULL, drag_clamp ? &s8_fifty : NULL); + ImGui::DragScalar("drag u8", ImGuiDataType_U8, &u8_v, drag_speed, drag_clamp ? &u8_zero : NULL, drag_clamp ? &u8_fifty : NULL, "%u ms"); + ImGui::DragScalar("drag s16", ImGuiDataType_S16, &s16_v, drag_speed, drag_clamp ? &s16_zero : NULL, drag_clamp ? &s16_fifty : NULL); + ImGui::DragScalar("drag u16", ImGuiDataType_U16, &u16_v, drag_speed, drag_clamp ? &u16_zero : NULL, drag_clamp ? &u16_fifty : NULL, "%u ms"); ImGui::DragScalar("drag s32", ImGuiDataType_S32, &s32_v, drag_speed, drag_clamp ? &s32_zero : NULL, drag_clamp ? &s32_fifty : NULL); ImGui::DragScalar("drag u32", ImGuiDataType_U32, &u32_v, drag_speed, drag_clamp ? &u32_zero : NULL, drag_clamp ? &u32_fifty : NULL, "%u ms"); ImGui::DragScalar("drag s64", ImGuiDataType_S64, &s64_v, drag_speed, drag_clamp ? &s64_zero : NULL, drag_clamp ? &s64_fifty : NULL); ImGui::DragScalar("drag u64", ImGuiDataType_U64, &u64_v, drag_speed, drag_clamp ? &u64_zero : NULL, drag_clamp ? &u64_fifty : NULL); ImGui::DragScalar("drag float", ImGuiDataType_Float, &f32_v, 0.005f, &f32_zero, &f32_one, "%f", 1.0f); - ImGui::DragScalar("drag float ^2", ImGuiDataType_Float, &f32_v, 0.005f, &f32_zero, &f32_one, "%f", 2.0f); ImGui::SameLine(); ShowHelpMarker("You can use the 'power' parameter to increase tweaking precision on one side of the range."); + ImGui::DragScalar("drag float ^2", ImGuiDataType_Float, &f32_v, 0.005f, &f32_zero, &f32_one, "%f", 2.0f); ImGui::SameLine(); HelpMarker("You can use the 'power' parameter to increase tweaking precision on one side of the range."); ImGui::DragScalar("drag double", ImGuiDataType_Double, &f64_v, 0.0005f, &f64_zero, NULL, "%.10f grams", 1.0f); ImGui::DragScalar("drag double ^2", ImGuiDataType_Double, &f64_v, 0.0005f, &f64_zero, &f64_one, "0 < %.10f < 1", 2.0f); ImGui::Text("Sliders"); + ImGui::SliderScalar("slider s8 full", ImGuiDataType_S8, &s8_v, &s8_min, &s8_max, "%d"); + ImGui::SliderScalar("slider u8 full", ImGuiDataType_U8, &u8_v, &u8_min, &u8_max, "%u"); + ImGui::SliderScalar("slider s16 full", ImGuiDataType_S16, &s16_v, &s16_min, &s16_max, "%d"); + ImGui::SliderScalar("slider u16 full", ImGuiDataType_U16, &u16_v, &u16_min, &u16_max, "%u"); ImGui::SliderScalar("slider s32 low", ImGuiDataType_S32, &s32_v, &s32_zero, &s32_fifty,"%d"); ImGui::SliderScalar("slider s32 high", ImGuiDataType_S32, &s32_v, &s32_hi_a, &s32_hi_b, "%d"); ImGui::SliderScalar("slider s32 full", ImGuiDataType_S32, &s32_v, &s32_min, &s32_max, "%d"); @@ -1213,6 +1322,10 @@ static void ShowDemoWindowWidgets() static bool inputs_step = true; ImGui::Text("Inputs"); ImGui::Checkbox("Show step buttons", &inputs_step); + ImGui::InputScalar("input s8", ImGuiDataType_S8, &s8_v, inputs_step ? &s8_one : NULL, NULL, "%d"); + ImGui::InputScalar("input u8", ImGuiDataType_U8, &u8_v, inputs_step ? &u8_one : NULL, NULL, "%u"); + ImGui::InputScalar("input s16", ImGuiDataType_S16, &s16_v, inputs_step ? &s16_one : NULL, NULL, "%d"); + ImGui::InputScalar("input u16", ImGuiDataType_U16, &u16_v, inputs_step ? &u16_one : NULL, NULL, "%u"); ImGui::InputScalar("input s32", ImGuiDataType_S32, &s32_v, inputs_step ? &s32_one : NULL, NULL, "%d"); ImGui::InputScalar("input s32 hex", ImGuiDataType_S32, &s32_v, inputs_step ? &s32_one : NULL, NULL, "%08X", ImGuiInputTextFlags_CharsHexadecimal); ImGui::InputScalar("input u32", ImGuiDataType_U32, &u32_v, inputs_step ? &u32_one : NULL, NULL, "%u"); @@ -1347,7 +1460,7 @@ static void ShowDemoWindowWidgets() static int mode = 0; if (ImGui::RadioButton("Copy", mode == Mode_Copy)) { mode = Mode_Copy; } ImGui::SameLine(); if (ImGui::RadioButton("Move", mode == Mode_Move)) { mode = Mode_Move; } ImGui::SameLine(); - if (ImGui::RadioButton("Swap", mode == Mode_Swap)) { mode = Mode_Swap; } + if (ImGui::RadioButton("Swap", mode == Mode_Swap)) { mode = Mode_Swap; } static const char* names[9] = { "Bobby", "Beatrice", "Betty", "Brianna", "Barry", "Bernard", "Bibi", "Blaine", "Bryn" }; for (int n = 0; n < IM_ARRAYSIZE(names); n++) { @@ -1404,20 +1517,23 @@ static void ShowDemoWindowWidgets() static int item_type = 1; static bool b = false; static float col4f[4] = { 1.0f, 0.5, 0.0f, 1.0f }; + static char str[16] = {}; ImGui::RadioButton("Text", &item_type, 0); ImGui::RadioButton("Button", &item_type, 1); - ImGui::RadioButton("CheckBox", &item_type, 2); + ImGui::RadioButton("Checkbox", &item_type, 2); ImGui::RadioButton("SliderFloat", &item_type, 3); - ImGui::RadioButton("ColorEdit4", &item_type, 4); - ImGui::RadioButton("ListBox", &item_type, 5); + ImGui::RadioButton("InputText", &item_type, 4); + ImGui::RadioButton("ColorEdit4", &item_type, 5); + ImGui::RadioButton("ListBox", &item_type, 6); ImGui::Separator(); bool ret = false; if (item_type == 0) { ImGui::Text("ITEM: Text"); } // Testing text items with no identifier/interaction if (item_type == 1) { ret = ImGui::Button("ITEM: Button"); } // Testing button - if (item_type == 2) { ret = ImGui::Checkbox("ITEM: CheckBox", &b); } // Testing checkbox + if (item_type == 2) { ret = ImGui::Checkbox("ITEM: Checkbox", &b); } // Testing checkbox if (item_type == 3) { ret = ImGui::SliderFloat("ITEM: SliderFloat", &col4f[0], 0.0f, 1.0f); } // Testing basic item - if (item_type == 4) { ret = ImGui::ColorEdit4("ITEM: ColorEdit4", col4f); } // Testing multi-component items (IsItemXXX flags are reported merged) - if (item_type == 5) { const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::ListBox("ITEM: ListBox", ¤t, items, IM_ARRAYSIZE(items), IM_ARRAYSIZE(items)); } + if (item_type == 4) { ret = ImGui::InputText("ITEM: InputText", &str[0], IM_ARRAYSIZE(str)); } // Testing input text (which handles tabbing) + if (item_type == 5) { ret = ImGui::ColorEdit4("ITEM: ColorEdit4", col4f); } // Testing multi-component items (IsItemXXX flags are reported merged) + if (item_type == 6) { const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::ListBox("ITEM: ListBox", ¤t, items, IM_ARRAYSIZE(items), IM_ARRAYSIZE(items)); } ImGui::BulletText( "Return value = %d\n" "IsItemFocused() = %d\n" @@ -1428,6 +1544,7 @@ static void ShowDemoWindowWidgets() "IsItemHovered(_RectOnly) = %d\n" "IsItemActive() = %d\n" "IsItemEdited() = %d\n" + "IsItemActivated() = %d\n" "IsItemDeactivated() = %d\n" "IsItemDeactivatedEdit() = %d\n" "IsItemVisible() = %d\n" @@ -1443,6 +1560,7 @@ static void ShowDemoWindowWidgets() ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly), ImGui::IsItemActive(), ImGui::IsItemEdited(), + ImGui::IsItemActivated(), ImGui::IsItemDeactivated(), ImGui::IsItemDeactivatedAfterEdit(), ImGui::IsItemVisible(), @@ -1487,12 +1605,12 @@ static void ShowDemoWindowWidgets() ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow)); ImGui::BeginChild("child", ImVec2(0, 50), true); - ImGui::Text("This is another child window for testing with the _ChildWindows flag."); + ImGui::Text("This is another child window for testing the _ChildWindows flag."); ImGui::EndChild(); if (embed_all_inside_a_child_window) ImGui::EndChild(); - // Calling IsItemHovered() after begin returns the hovered status of the title bar. + // Calling IsItemHovered() after begin returns the hovered status of the title bar. // This is useful in particular if you want to create a context menu (with BeginPopupContextItem) associated to the title bar of a window. static bool test_window = false; ImGui::Checkbox("Hovered/Active tests after Begin() for title bar testing", &test_window); @@ -1520,8 +1638,9 @@ static void ShowDemoWindowLayout() if (!ImGui::CollapsingHeader("Layout")) return; - if (ImGui::TreeNode("Child regions")) + if (ImGui::TreeNode("Child windows")) { + HelpMarker("Use child windows to begin into a self-contained independent scrolling/clipping regions within a host window."); static bool disable_mouse_wheel = false; static bool disable_menu = false; ImGui::Checkbox("Disable Mouse Wheel", &disable_mouse_wheel); @@ -1536,7 +1655,8 @@ static void ShowDemoWindowLayout() // Child 1: no border, enable horizontal scrollbar { - ImGui::BeginChild("Child1", ImVec2(ImGui::GetWindowContentRegionWidth() * 0.5f, 300), false, ImGuiWindowFlags_HorizontalScrollbar | (disable_mouse_wheel ? ImGuiWindowFlags_NoScrollWithMouse : 0)); + ImGuiWindowFlags window_flags = ImGuiWindowFlags_HorizontalScrollbar | (disable_mouse_wheel ? ImGuiWindowFlags_NoScrollWithMouse : 0); + ImGui::BeginChild("Child1", ImVec2(ImGui::GetWindowContentRegionWidth() * 0.5f, 260), false, window_flags); for (int i = 0; i < 100; i++) { ImGui::Text("%04d: scrollable region", i); @@ -1552,8 +1672,9 @@ static void ShowDemoWindowLayout() // Child 2: rounded border { + ImGuiWindowFlags window_flags = (disable_mouse_wheel ? ImGuiWindowFlags_NoScrollWithMouse : 0) | (disable_menu ? 0 : ImGuiWindowFlags_MenuBar); ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 5.0f); - ImGui::BeginChild("Child2", ImVec2(0, 300), true, (disable_mouse_wheel ? ImGuiWindowFlags_NoScrollWithMouse : 0) | (disable_menu ? 0 : ImGuiWindowFlags_MenuBar)); + ImGui::BeginChild("Child2", ImVec2(0, 260), true, window_flags); if (!disable_menu && ImGui::BeginMenuBar()) { if (ImGui::BeginMenu("Menu")) @@ -1575,6 +1696,27 @@ static void ShowDemoWindowLayout() ImGui::PopStyleVar(); } + ImGui::Separator(); + + // Demonstrate a few extra things + // - Changing ImGuiCol_ChildBg (which is transparent black in default styles) + // - Using SetCursorPos() to position the child window (because the child window is an item from the POV of the parent window) + // You can also call SetNextWindowPos() to position the child window. The parent window will effectively layout from this position. + // - Using ImGui::GetItemRectMin/Max() to query the "item" state (because the child window is an item from the POV of the parent window) + // See "Widgets" -> "Querying Status (Active/Focused/Hovered etc.)" section for more details about this. + { + ImGui::SetCursorPosX(50); + ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(255, 0, 0, 100)); + ImGui::BeginChild("blah", ImVec2(200, 100), true, ImGuiWindowFlags_None); + for (int n = 0; n < 50; n++) + ImGui::Text("Some test %d", n); + ImGui::EndChild(); + ImVec2 child_rect_min = ImGui::GetItemRectMin(); + ImVec2 child_rect_max = ImGui::GetItemRectMax(); + ImGui::PopStyleColor(); + ImGui::Text("Rect of child window is: (%.0f,%.0f) (%.0f,%.0f)", child_rect_min.x, child_rect_min.y, child_rect_max.x, child_rect_max.y); + } + ImGui::TreePop(); } @@ -1582,31 +1724,31 @@ static void ShowDemoWindowLayout() { static float f = 0.0f; ImGui::Text("PushItemWidth(100)"); - ImGui::SameLine(); ShowHelpMarker("Fixed width."); + ImGui::SameLine(); HelpMarker("Fixed width."); ImGui::PushItemWidth(100); ImGui::DragFloat("float##1", &f); ImGui::PopItemWidth(); ImGui::Text("PushItemWidth(GetWindowWidth() * 0.5f)"); - ImGui::SameLine(); ShowHelpMarker("Half of window width."); + ImGui::SameLine(); HelpMarker("Half of window width."); ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.5f); ImGui::DragFloat("float##2", &f); ImGui::PopItemWidth(); ImGui::Text("PushItemWidth(GetContentRegionAvailWidth() * 0.5f)"); - ImGui::SameLine(); ShowHelpMarker("Half of available width.\n(~ right-cursor_pos)\n(works within a column set)"); + ImGui::SameLine(); HelpMarker("Half of available width.\n(~ right-cursor_pos)\n(works within a column set)"); ImGui::PushItemWidth(ImGui::GetContentRegionAvailWidth() * 0.5f); ImGui::DragFloat("float##3", &f); ImGui::PopItemWidth(); ImGui::Text("PushItemWidth(-100)"); - ImGui::SameLine(); ShowHelpMarker("Align to right edge minus 100"); + ImGui::SameLine(); HelpMarker("Align to right edge minus 100"); ImGui::PushItemWidth(-100); ImGui::DragFloat("float##4", &f); ImGui::PopItemWidth(); ImGui::Text("PushItemWidth(-1)"); - ImGui::SameLine(); ShowHelpMarker("Align to right edge"); + ImGui::SameLine(); HelpMarker("Align to right edge"); ImGui::PushItemWidth(-1); ImGui::DragFloat("float##5", &f); ImGui::PopItemWidth(); @@ -1702,9 +1844,80 @@ static void ShowDemoWindowLayout() ImGui::TreePop(); } + if (ImGui::TreeNode("Tabs")) + { + if (ImGui::TreeNode("Basic")) + { + ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_None; + if (ImGui::BeginTabBar("MyTabBar", tab_bar_flags)) + { + if (ImGui::BeginTabItem("Avocado")) + { + ImGui::Text("This is the Avocado tab!\nblah blah blah blah blah"); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Broccoli")) + { + ImGui::Text("This is the Broccoli tab!\nblah blah blah blah blah"); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Cucumber")) + { + ImGui::Text("This is the Cucumber tab!\nblah blah blah blah blah"); + ImGui::EndTabItem(); + } + ImGui::EndTabBar(); + } + ImGui::Separator(); + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Advanced & Close Button")) + { + // Expose a couple of the available flags. In most cases you may just call BeginTabBar() with no flags (0). + static ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_Reorderable; + ImGui::CheckboxFlags("ImGuiTabBarFlags_Reorderable", (unsigned int*)&tab_bar_flags, ImGuiTabBarFlags_Reorderable); + ImGui::CheckboxFlags("ImGuiTabBarFlags_AutoSelectNewTabs", (unsigned int*)&tab_bar_flags, ImGuiTabBarFlags_AutoSelectNewTabs); + ImGui::CheckboxFlags("ImGuiTabBarFlags_TabListPopupButton", (unsigned int*)&tab_bar_flags, ImGuiTabBarFlags_TabListPopupButton); + ImGui::CheckboxFlags("ImGuiTabBarFlags_NoCloseWithMiddleMouseButton", (unsigned int*)&tab_bar_flags, ImGuiTabBarFlags_NoCloseWithMiddleMouseButton); + if ((tab_bar_flags & ImGuiTabBarFlags_FittingPolicyMask_) == 0) + tab_bar_flags |= ImGuiTabBarFlags_FittingPolicyDefault_; + if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyResizeDown", (unsigned int*)&tab_bar_flags, ImGuiTabBarFlags_FittingPolicyResizeDown)) + tab_bar_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyResizeDown); + if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyScroll", (unsigned int*)&tab_bar_flags, ImGuiTabBarFlags_FittingPolicyScroll)) + tab_bar_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyScroll); + + // Tab Bar + const char* names[4] = { "Artichoke", "Beetroot", "Celery", "Daikon" }; + static bool opened[4] = { true, true, true, true }; // Persistent user state + for (int n = 0; n < IM_ARRAYSIZE(opened); n++) + { + if (n > 0) { ImGui::SameLine(); } + ImGui::Checkbox(names[n], &opened[n]); + } + + // Passing a bool* to BeginTabItem() is similar to passing one to Begin(): the underlying bool will be set to false when the tab is closed. + if (ImGui::BeginTabBar("MyTabBar", tab_bar_flags)) + { + for (int n = 0; n < IM_ARRAYSIZE(opened); n++) + if (opened[n] && ImGui::BeginTabItem(names[n], &opened[n])) + { + ImGui::Text("This is the %s tab!", names[n]); + if (n & 1) + ImGui::Text("I am an odd tab."); + ImGui::EndTabItem(); + } + ImGui::EndTabBar(); + } + ImGui::Separator(); + ImGui::TreePop(); + } + ImGui::TreePop(); + } + if (ImGui::TreeNode("Groups")) { - ImGui::TextWrapped("(Using ImGui::BeginGroup()/EndGroup() to layout items. BeginGroup() basically locks the horizontal position. EndGroup() bundles the whole group so that you can use functions such as IsItemHovered() on it.)"); + HelpMarker("Using ImGui::BeginGroup()/EndGroup() to layout items. BeginGroup() basically locks the horizontal position. EndGroup() bundles the whole group so that you can use functions such as IsItemHovered() on it."); ImGui::BeginGroup(); { ImGui::BeginGroup(); @@ -1748,7 +1961,7 @@ static void ShowDemoWindowLayout() if (ImGui::TreeNode("Text Baseline Alignment")) { - ImGui::TextWrapped("(This is testing the vertical alignment that occurs on text to keep it at the same baseline as widgets. Lines only composed of text or \"small\" widgets fit in less vertical spaces than lines with normal widgets)"); + HelpMarker("This is testing the vertical alignment that gets applied on text to keep it aligned with widgets. Lines only composed of text or \"small\" widgets fit in less vertical spaces than lines with normal widgets."); ImGui::Text("One\nTwo\nThree"); ImGui::SameLine(); ImGui::Text("Hello\nWorld"); ImGui::SameLine(); @@ -1803,7 +2016,8 @@ static void ShowDemoWindowLayout() if (ImGui::TreeNode("Scrolling")) { - ImGui::TextWrapped("(Use SetScrollHereY() or SetScrollFromPosY() to scroll to a given position.)"); + HelpMarker("Use SetScrollHereY() or SetScrollFromPosY() to scroll to a given position."); + static bool track = true; static int track_line = 50, scroll_to_px = 200; ImGui::Checkbox("Track", &track); @@ -1844,8 +2058,7 @@ static void ShowDemoWindowLayout() if (ImGui::TreeNode("Horizontal Scrolling")) { - ImGui::Bullet(); ImGui::TextWrapped("Horizontal scrolling for a window has to be enabled explicitly via the ImGuiWindowFlags_HorizontalScrollbar flag."); - ImGui::Bullet(); ImGui::TextWrapped("You may want to explicitly specify content width by calling SetNextWindowContentWidth() before Begin()."); + HelpMarker("Horizontal scrolling for a window has to be enabled explicitly via the ImGuiWindowFlags_HorizontalScrollbar flag.\n\nYou may want to explicitly specify content width by calling SetNextWindowContentWidth() before Begin()."); static int lines = 7; ImGui::SliderInt("Lines", &lines, 1, 15); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f); @@ -1876,15 +2089,15 @@ static void ShowDemoWindowLayout() ImGui::EndChild(); ImGui::PopStyleVar(2); float scroll_x_delta = 0.0f; - ImGui::SmallButton("<<"); if (ImGui::IsItemActive()) scroll_x_delta = -ImGui::GetIO().DeltaTime * 1000.0f; ImGui::SameLine(); + ImGui::SmallButton("<<"); if (ImGui::IsItemActive()) { scroll_x_delta = -ImGui::GetIO().DeltaTime * 1000.0f; } ImGui::SameLine(); ImGui::Text("Scroll from code"); ImGui::SameLine(); - ImGui::SmallButton(">>"); if (ImGui::IsItemActive()) scroll_x_delta = +ImGui::GetIO().DeltaTime * 1000.0f; ImGui::SameLine(); + ImGui::SmallButton(">>"); if (ImGui::IsItemActive()) { scroll_x_delta = +ImGui::GetIO().DeltaTime * 1000.0f; } ImGui::SameLine(); ImGui::Text("%.0f/%.0f", scroll_x, scroll_max_x); if (scroll_x_delta != 0.0f) { ImGui::BeginChild("scrolling"); // Demonstrate a trick: you can use Begin to set yourself in the context of another window (here we are already out of your child window) ImGui::SetScrollX(ImGui::GetScrollX() + scroll_x_delta); - ImGui::End(); + ImGui::EndChild(); } ImGui::TreePop(); } @@ -1893,7 +2106,7 @@ static void ShowDemoWindowLayout() { static ImVec2 size(100, 100), offset(50, 20); ImGui::TextWrapped("On a per-widget basis we are occasionally clipping text CPU-side if it won't fit in its frame. Otherwise we are doing coarser clipping + passing a scissor rectangle to the renderer. The system is designed to try minimizing both execution and CPU/GPU rendering cost."); - ImGui::DragFloat2("size", (float*)&size, 0.5f, 0.0f, 200.0f, "%.0f"); + ImGui::DragFloat2("size", (float*)&size, 0.5f, 1.0f, 200.0f, "%.0f"); ImGui::TextWrapped("(Click and drag)"); ImVec2 pos = ImGui::GetCursorScreenPos(); ImVec4 clip_rect(pos.x, pos.y, pos.x + size.x, pos.y + size.y); @@ -1910,12 +2123,13 @@ static void ShowDemoWindowPopups() if (!ImGui::CollapsingHeader("Popups & Modal windows")) return; - // Popups are windows with a few special properties: + // The properties of popups windows are: // - They block normal mouse hovering detection outside them. (*) // - Unless modal, they can be closed by clicking anywhere outside them, or by pressing ESCAPE. // - Their visibility state (~bool) is held internally by imgui instead of being held by the programmer as we are used to with regular Begin() calls. + // User can manipulate the visibility state by calling OpenPopup(). // (*) One can use IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup) to bypass it and detect hovering even when normally blocked by a popup. - // Those three properties are intimately connected. The library needs to hold their visibility state because it can close popups at any time. + // Those three properties are connected. The library needs to hold their visibility state because it can close popups at any time. // Typical use for regular windows: // bool my_tool_is_active = false; if (ImGui::Button("Open")) my_tool_is_active = true; [...] if (my_tool_is_active) Begin("My Tool", &my_tool_is_active) { [...] } End(); @@ -1923,7 +2137,7 @@ static void ShowDemoWindowPopups() // if (ImGui::Button("Open")) ImGui::OpenPopup("MyPopup"); if (ImGui::BeginPopup("MyPopup") { [...] EndPopup(); } // With popups we have to go through a library call (here OpenPopup) to manipulate the visibility state. - // This may be a bit confusing at first but it should quickly make sense. Follow on the examples below. + // This may be a bit confusing at first but it should quickly make sense. Follow on the examples below. if (ImGui::TreeNode("Popups")) { @@ -2016,10 +2230,10 @@ static void ShowDemoWindowPopups() // We can also use OpenPopupOnItemClick() which is the same as BeginPopupContextItem() but without the Begin call. // So here we will make it that clicking on the text field with the right mouse button (1) will toggle the visibility of the popup above. - ImGui::Text("(You can also right-click me to the same popup as above.)"); + ImGui::Text("(You can also right-click me to open the same popup as above.)"); ImGui::OpenPopupOnItemClick("item context menu", 1); - // When used after an item that has an ID (here the Button), we can skip providing an ID to BeginPopupContextItem(). + // When used after an item that has an ID (here the Button), we can skip providing an ID to BeginPopupContextItem(). // BeginPopupContextItem() will use the last item ID as the popup ID. // In addition here, we want to include your editable label inside the button label. We use the ### operator to override the ID (read FAQ about ID for details) static char name[32] = "Label1"; @@ -2044,6 +2258,7 @@ static void ShowDemoWindowPopups() if (ImGui::Button("Delete..")) ImGui::OpenPopup("Delete?"); + if (ImGui::BeginPopupModal("Delete?", NULL, ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::Text("All those beautiful files will be deleted.\nThis operation cannot be undone!\n\n"); @@ -2066,17 +2281,32 @@ static void ShowDemoWindowPopups() if (ImGui::Button("Stacked modals..")) ImGui::OpenPopup("Stacked 1"); - if (ImGui::BeginPopupModal("Stacked 1")) + if (ImGui::BeginPopupModal("Stacked 1", NULL, ImGuiWindowFlags_MenuBar)) { + if (ImGui::BeginMenuBar()) + { + if (ImGui::BeginMenu("File")) + { + if (ImGui::MenuItem("Dummy menu item")) {} + ImGui::EndMenu(); + } + ImGui::EndMenuBar(); + } ImGui::Text("Hello from Stacked The First\nUsing style.Colors[ImGuiCol_ModalWindowDimBg] behind it."); + + // Testing behavior of widgets stacking their own regular popups over the modal. static int item = 1; - ImGui::Combo("Combo", &item, "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0"); static float color[4] = { 0.4f,0.7f,0.0f,0.5f }; - ImGui::ColorEdit4("color", color); // This is to test behavior of stacked regular popups over a modal + ImGui::Combo("Combo", &item, "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0"); + ImGui::ColorEdit4("color", color); if (ImGui::Button("Add another modal..")) ImGui::OpenPopup("Stacked 2"); - if (ImGui::BeginPopupModal("Stacked 2")) + + // Also demonstrate passing a bool* to BeginPopupModal(), this will create a regular close button which will close the popup. + // Note that the visibility state of popups is owned by imgui, so the input value of the bool actually doesn't matter here. + bool dummy_open = true; + if (ImGui::BeginPopupModal("Stacked 2", &dummy_open)) { ImGui::Text("Hello from Stacked The Second!"); if (ImGui::Button("Close")) @@ -2281,7 +2511,7 @@ static void ShowDemoWindowColumns() } bool node_open = ImGui::TreeNode("Tree within single cell"); - ImGui::SameLine(); ShowHelpMarker("NB: Tree node must be poped before ending the cell. There's no storage of state per-cell."); + ImGui::SameLine(); HelpMarker("NB: Tree node must be poped before ending the cell. There's no storage of state per-cell."); if (node_open) { ImGui::Columns(2, "tree items"); @@ -2364,7 +2594,7 @@ static void ShowDemoWindowMisc() ImGui::InputText("3", buf, IM_ARRAYSIZE(buf)); ImGui::PushAllowKeyboardFocus(false); ImGui::InputText("4 (tab skip)", buf, IM_ARRAYSIZE(buf)); - //ImGui::SameLine(); ShowHelperMarker("Use ImGui::PushAllowKeyboardFocus(bool)\nto disable tabbing through certain widgets."); + //ImGui::SameLine(); HelpMarker("Use ImGui::PushAllowKeyboardFocus(bool)\nto disable tabbing through certain widgets."); ImGui::PopAllowKeyboardFocus(); ImGui::InputText("5", buf, IM_ARRAYSIZE(buf)); ImGui::TreePop(); @@ -2400,9 +2630,9 @@ static void ShowDemoWindowMisc() // Use >= 0 parameter to SetKeyboardFocusHere() to focus an upcoming item static float f3[3] = { 0.0f, 0.0f, 0.0f }; int focus_ahead = -1; - if (ImGui::Button("Focus on X")) focus_ahead = 0; ImGui::SameLine(); - if (ImGui::Button("Focus on Y")) focus_ahead = 1; ImGui::SameLine(); - if (ImGui::Button("Focus on Z")) focus_ahead = 2; + if (ImGui::Button("Focus on X")) { focus_ahead = 0; } ImGui::SameLine(); + if (ImGui::Button("Focus on Y")) { focus_ahead = 1; } ImGui::SameLine(); + if (ImGui::Button("Focus on Z")) { focus_ahead = 2; } if (focus_ahead != -1) ImGui::SetKeyboardFocusHere(focus_ahead); ImGui::SliderFloat3("Float3", &f3[0], 0.0f, 1.0f); @@ -2442,7 +2672,7 @@ static void ShowDemoWindowMisc() ImGui::Text("Current mouse cursor = %d: %s", ImGui::GetMouseCursor(), mouse_cursors_names[ImGui::GetMouseCursor()]); ImGui::Text("Hover to see mouse cursors:"); - ImGui::SameLine(); ShowHelpMarker("Your application can render a different mouse cursor based on what ImGui::GetMouseCursor() returns. If software cursor rendering (io.MouseDrawCursor) is set ImGui will draw the right cursor for you, otherwise your backend needs to handle it."); + ImGui::SameLine(); HelpMarker("Your application can render a different mouse cursor based on what ImGui::GetMouseCursor() returns. If software cursor rendering (io.MouseDrawCursor) is set ImGui will draw the right cursor for you, otherwise your backend needs to handle it."); for (int i = 0; i < ImGuiMouseCursor_COUNT; i++) { char label[32]; @@ -2456,6 +2686,129 @@ static void ShowDemoWindowMisc() } } +//----------------------------------------------------------------------------- +// [SECTION] About Window / ShowAboutWindow() +// Access from ImGui Demo -> Help -> About +//----------------------------------------------------------------------------- + +void ImGui::ShowAboutWindow(bool* p_open) +{ + if (!ImGui::Begin("About Dear ImGui", p_open, ImGuiWindowFlags_AlwaysAutoResize)) + { + ImGui::End(); + return; + } + ImGui::Text("Dear ImGui %s", ImGui::GetVersion()); + ImGui::Separator(); + ImGui::Text("By Omar Cornut and all dear imgui contributors."); + ImGui::Text("Dear ImGui is licensed under the MIT License, see LICENSE for more information."); + + static bool show_config_info = false; + ImGui::Checkbox("Config/Build Information", &show_config_info); + if (show_config_info) + { + ImGuiIO& io = ImGui::GetIO(); + ImGuiStyle& style = ImGui::GetStyle(); + + bool copy_to_clipboard = ImGui::Button("Copy to clipboard"); + ImGui::BeginChildFrame(ImGui::GetID("cfginfos"), ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * 18), ImGuiWindowFlags_NoMove); + if (copy_to_clipboard) + ImGui::LogToClipboard(); + + ImGui::Text("Dear ImGui %s (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM); + ImGui::Separator(); + ImGui::Text("sizeof(size_t): %d, sizeof(ImDrawIdx): %d, sizeof(ImDrawVert): %d", (int)sizeof(size_t), (int)sizeof(ImDrawIdx), (int)sizeof(ImDrawVert)); + ImGui::Text("define: __cplusplus=%d", (int)__cplusplus); +#ifdef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + ImGui::Text("define: IMGUI_DISABLE_OBSOLETE_FUNCTIONS"); +#endif +#ifdef IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS + ImGui::Text("define: IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS"); +#endif +#ifdef IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS + ImGui::Text("define: IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS"); +#endif +#ifdef IMGUI_DISABLE_WIN32_FUNCTIONS + ImGui::Text("define: IMGUI_DISABLE_WIN32_FUNCTIONS"); +#endif +#ifdef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS + ImGui::Text("define: IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS"); +#endif +#ifdef IMGUI_DISABLE_MATH_FUNCTIONS + ImGui::Text("define: IMGUI_DISABLE_MATH_FUNCTIONS"); +#endif +#ifdef IMGUI_DISABLE_DEFAULT_ALLOCATORS + ImGui::Text("define: IMGUI_DISABLE_DEFAULT_ALLOCATORS"); +#endif +#ifdef IMGUI_USE_BGRA_PACKED_COLOR + ImGui::Text("define: IMGUI_USE_BGRA_PACKED_COLOR"); +#endif +#ifdef _WIN32 + ImGui::Text("define: _WIN32"); +#endif +#ifdef _WIN64 + ImGui::Text("define: _WIN64"); +#endif +#ifdef __linux__ + ImGui::Text("define: __linux__"); +#endif +#ifdef __APPLE__ + ImGui::Text("define: __APPLE__"); +#endif +#ifdef _MSC_VER + ImGui::Text("define: _MSC_VER=%d", _MSC_VER); +#endif +#ifdef __MINGW32__ + ImGui::Text("define: __MINGW32__"); +#endif +#ifdef __MINGW64__ + ImGui::Text("define: __MINGW64__"); +#endif +#ifdef __GNUC__ + ImGui::Text("define: __GNUC__=%d", (int)__GNUC__); +#endif +#ifdef __clang_version__ + ImGui::Text("define: __clang_version__=%s", __clang_version__); +#endif + ImGui::Separator(); + ImGui::Text("io.BackendPlatformName: %s", io.BackendPlatformName ? io.BackendPlatformName : "NULL"); + ImGui::Text("io.BackendRendererName: %s", io.BackendRendererName ? io.BackendRendererName : "NULL"); + ImGui::Text("io.ConfigFlags: 0x%08X", io.ConfigFlags); + if (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) ImGui::Text(" NavEnableKeyboard"); + if (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) ImGui::Text(" NavEnableGamepad"); + if (io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) ImGui::Text(" NavEnableSetMousePos"); + if (io.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard) ImGui::Text(" NavNoCaptureKeyboard"); + if (io.ConfigFlags & ImGuiConfigFlags_NoMouse) ImGui::Text(" NoMouse"); + if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) ImGui::Text(" NoMouseCursorChange"); + if (io.MouseDrawCursor) ImGui::Text("io.MouseDrawCursor"); + if (io.ConfigMacOSXBehaviors) ImGui::Text("io.ConfigMacOSXBehaviors"); + if (io.ConfigInputTextCursorBlink) ImGui::Text("io.ConfigInputTextCursorBlink"); + if (io.ConfigWindowsResizeFromEdges) ImGui::Text("io.ConfigWindowsResizeFromEdges"); + if (io.ConfigWindowsMoveFromTitleBarOnly) ImGui::Text("io.ConfigWindowsMoveFromTitleBarOnly"); + ImGui::Text("io.BackendFlags: 0x%08X", io.BackendFlags); + if (io.BackendFlags & ImGuiBackendFlags_HasGamepad) ImGui::Text(" HasGamepad"); + if (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) ImGui::Text(" HasMouseCursors"); + if (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos) ImGui::Text(" HasSetMousePos"); + ImGui::Separator(); + ImGui::Text("io.Fonts: %d fonts, Flags: 0x%08X, TexSize: %d,%d", io.Fonts->Fonts.Size, io.Fonts->Flags, io.Fonts->TexWidth, io.Fonts->TexHeight); + ImGui::Text("io.DisplaySize: %.2f,%.2f", io.DisplaySize.x, io.DisplaySize.y); + ImGui::Text("io.DisplayFramebufferScale: %.2f,%.2f", io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y); + ImGui::Separator(); + ImGui::Text("style.WindowPadding: %.2f,%.2f", style.WindowPadding.x, style.WindowPadding.y); + ImGui::Text("style.WindowBorderSize: %.2f", style.WindowBorderSize); + ImGui::Text("style.FramePadding: %.2f,%.2f", style.FramePadding.x, style.FramePadding.y); + ImGui::Text("style.FrameRounding: %.2f", style.FrameRounding); + ImGui::Text("style.FrameBorderSize: %.2f", style.FrameBorderSize); + ImGui::Text("style.ItemSpacing: %.2f,%.2f", style.ItemSpacing.x, style.ItemSpacing.y); + ImGui::Text("style.ItemInnerSpacing: %.2f,%.2f", style.ItemInnerSpacing.x, style.ItemInnerSpacing.y); + + if (copy_to_clipboard) + ImGui::LogFinish(); + ImGui::EndChildFrame(); + } + ImGui::End(); +} + //----------------------------------------------------------------------------- // [SECTION] Style Editor / ShowStyleEditor() //----------------------------------------------------------------------------- @@ -2487,12 +2840,17 @@ void ImGui::ShowFontSelector(const char* label) if (ImGui::BeginCombo(label, font_current->GetDebugName())) { for (int n = 0; n < io.Fonts->Fonts.Size; n++) - if (ImGui::Selectable(io.Fonts->Fonts[n]->GetDebugName(), io.Fonts->Fonts[n] == font_current)) - io.FontDefault = io.Fonts->Fonts[n]; + { + ImFont* font = io.Fonts->Fonts[n]; + ImGui::PushID((void*)font); + if (ImGui::Selectable(font->GetDebugName(), font == font_current)) + io.FontDefault = font; + ImGui::PopID(); + } ImGui::EndCombo(); } ImGui::SameLine(); - ShowHelpMarker( + HelpMarker( "- Load additional fonts with io.Fonts->AddFontFromFileTTF().\n" "- The font atlas is built when calling io.Fonts->GetTexDataAsXXXX() or io.Fonts->Build().\n" "- Read FAQ and documentation in misc/fonts/ for more details.\n" @@ -2535,188 +2893,203 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) if (ImGui::Button("Revert Ref")) style = *ref; ImGui::SameLine(); - ShowHelpMarker("Save/Revert in local non-persistent storage. Default Colors definition are not affected. Use \"Export Colors\" below to save them somewhere."); + HelpMarker("Save/Revert in local non-persistent storage. Default Colors definition are not affected. Use \"Export Colors\" below to save them somewhere."); - if (ImGui::TreeNode("Rendering")) - { - ImGui::Checkbox("Anti-aliased lines", &style.AntiAliasedLines); ImGui::SameLine(); ShowHelpMarker("When disabling anti-aliasing lines, you'll probably want to disable borders in your style as well."); - ImGui::Checkbox("Anti-aliased fill", &style.AntiAliasedFill); - ImGui::PushItemWidth(100); - ImGui::DragFloat("Curve Tessellation Tolerance", &style.CurveTessellationTol, 0.02f, 0.10f, FLT_MAX, "%.2f", 2.0f); - if (style.CurveTessellationTol < 0.10f) style.CurveTessellationTol = 0.10f; - ImGui::DragFloat("Global Alpha", &style.Alpha, 0.005f, 0.20f, 1.0f, "%.2f"); // Not exposing zero here so user doesn't "lose" the UI (zero alpha clips all widgets). But application code could have a toggle to switch between zero and non-zero. - ImGui::PopItemWidth(); - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Settings")) - { - ImGui::SliderFloat2("WindowPadding", (float*)&style.WindowPadding, 0.0f, 20.0f, "%.0f"); - ImGui::SliderFloat("PopupRounding", &style.PopupRounding, 0.0f, 16.0f, "%.0f"); - ImGui::SliderFloat2("FramePadding", (float*)&style.FramePadding, 0.0f, 20.0f, "%.0f"); - ImGui::SliderFloat2("ItemSpacing", (float*)&style.ItemSpacing, 0.0f, 20.0f, "%.0f"); - ImGui::SliderFloat2("ItemInnerSpacing", (float*)&style.ItemInnerSpacing, 0.0f, 20.0f, "%.0f"); - ImGui::SliderFloat2("TouchExtraPadding", (float*)&style.TouchExtraPadding, 0.0f, 10.0f, "%.0f"); - ImGui::SliderFloat("IndentSpacing", &style.IndentSpacing, 0.0f, 30.0f, "%.0f"); - ImGui::SliderFloat("ScrollbarSize", &style.ScrollbarSize, 1.0f, 20.0f, "%.0f"); - ImGui::SliderFloat("GrabMinSize", &style.GrabMinSize, 1.0f, 20.0f, "%.0f"); - ImGui::Text("BorderSize"); - ImGui::SliderFloat("WindowBorderSize", &style.WindowBorderSize, 0.0f, 1.0f, "%.0f"); - ImGui::SliderFloat("ChildBorderSize", &style.ChildBorderSize, 0.0f, 1.0f, "%.0f"); - ImGui::SliderFloat("PopupBorderSize", &style.PopupBorderSize, 0.0f, 1.0f, "%.0f"); - ImGui::SliderFloat("FrameBorderSize", &style.FrameBorderSize, 0.0f, 1.0f, "%.0f"); - ImGui::Text("Rounding"); - ImGui::SliderFloat("WindowRounding", &style.WindowRounding, 0.0f, 14.0f, "%.0f"); - ImGui::SliderFloat("ChildRounding", &style.ChildRounding, 0.0f, 16.0f, "%.0f"); - ImGui::SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 12.0f, "%.0f"); - ImGui::SliderFloat("ScrollbarRounding", &style.ScrollbarRounding, 0.0f, 12.0f, "%.0f"); - ImGui::SliderFloat("GrabRounding", &style.GrabRounding, 0.0f, 12.0f, "%.0f"); - ImGui::Text("Alignment"); - ImGui::SliderFloat2("WindowTitleAlign", (float*)&style.WindowTitleAlign, 0.0f, 1.0f, "%.2f"); - ImGui::SliderFloat2("ButtonTextAlign", (float*)&style.ButtonTextAlign, 0.0f, 1.0f, "%.2f"); ImGui::SameLine(); ShowHelpMarker("Alignment applies when a button is larger than its text content."); - ImGui::Text("Safe Area Padding"); ImGui::SameLine(); ShowHelpMarker("Adjust if you cannot see the edges of your screen (e.g. on a TV where scaling has not been configured)."); - ImGui::SliderFloat2("DisplaySafeAreaPadding", (float*)&style.DisplaySafeAreaPadding, 0.0f, 30.0f, "%.0f"); - ImGui::TreePop(); - } + ImGui::Separator(); - if (ImGui::TreeNode("Colors")) + if (ImGui::BeginTabBar("##tabs", ImGuiTabBarFlags_None)) { - static int output_dest = 0; - static bool output_only_modified = true; - if (ImGui::Button("Export Unsaved")) + if (ImGui::BeginTabItem("Sizes")) { - if (output_dest == 0) - ImGui::LogToClipboard(); - else - ImGui::LogToTTY(); - ImGui::LogText("ImVec4* colors = ImGui::GetStyle().Colors;" IM_NEWLINE); - for (int i = 0; i < ImGuiCol_COUNT; i++) - { - const ImVec4& col = style.Colors[i]; - const char* name = ImGui::GetStyleColorName(i); - if (!output_only_modified || memcmp(&col, &ref->Colors[i], sizeof(ImVec4)) != 0) - ImGui::LogText("colors[ImGuiCol_%s]%*s= ImVec4(%.2ff, %.2ff, %.2ff, %.2ff);" IM_NEWLINE, name, 23-(int)strlen(name), "", col.x, col.y, col.z, col.w); - } - ImGui::LogFinish(); + ImGui::Text("Main"); + ImGui::SliderFloat2("WindowPadding", (float*)&style.WindowPadding, 0.0f, 20.0f, "%.0f"); + ImGui::SliderFloat2("FramePadding", (float*)&style.FramePadding, 0.0f, 20.0f, "%.0f"); + ImGui::SliderFloat2("ItemSpacing", (float*)&style.ItemSpacing, 0.0f, 20.0f, "%.0f"); + ImGui::SliderFloat2("ItemInnerSpacing", (float*)&style.ItemInnerSpacing, 0.0f, 20.0f, "%.0f"); + ImGui::SliderFloat2("TouchExtraPadding", (float*)&style.TouchExtraPadding, 0.0f, 10.0f, "%.0f"); + ImGui::SliderFloat("IndentSpacing", &style.IndentSpacing, 0.0f, 30.0f, "%.0f"); + ImGui::SliderFloat("ScrollbarSize", &style.ScrollbarSize, 1.0f, 20.0f, "%.0f"); + ImGui::SliderFloat("GrabMinSize", &style.GrabMinSize, 1.0f, 20.0f, "%.0f"); + ImGui::Text("Borders"); + ImGui::SliderFloat("WindowBorderSize", &style.WindowBorderSize, 0.0f, 1.0f, "%.0f"); + ImGui::SliderFloat("ChildBorderSize", &style.ChildBorderSize, 0.0f, 1.0f, "%.0f"); + ImGui::SliderFloat("PopupBorderSize", &style.PopupBorderSize, 0.0f, 1.0f, "%.0f"); + ImGui::SliderFloat("FrameBorderSize", &style.FrameBorderSize, 0.0f, 1.0f, "%.0f"); + ImGui::SliderFloat("TabBorderSize", &style.TabBorderSize, 0.0f, 1.0f, "%.0f"); + ImGui::Text("Rounding"); + ImGui::SliderFloat("WindowRounding", &style.WindowRounding, 0.0f, 12.0f, "%.0f"); + ImGui::SliderFloat("ChildRounding", &style.ChildRounding, 0.0f, 12.0f, "%.0f"); + ImGui::SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 12.0f, "%.0f"); + ImGui::SliderFloat("PopupRounding", &style.PopupRounding, 0.0f, 12.0f, "%.0f"); + ImGui::SliderFloat("ScrollbarRounding", &style.ScrollbarRounding, 0.0f, 12.0f, "%.0f"); + ImGui::SliderFloat("GrabRounding", &style.GrabRounding, 0.0f, 12.0f, "%.0f"); + ImGui::SliderFloat("TabRounding", &style.TabRounding, 0.0f, 12.0f, "%.0f"); + ImGui::Text("Alignment"); + ImGui::SliderFloat2("WindowTitleAlign", (float*)&style.WindowTitleAlign, 0.0f, 1.0f, "%.2f"); + ImGui::SliderFloat2("ButtonTextAlign", (float*)&style.ButtonTextAlign, 0.0f, 1.0f, "%.2f"); ImGui::SameLine(); HelpMarker("Alignment applies when a button is larger than its text content."); + ImGui::SliderFloat2("SelectableTextAlign", (float*)&style.SelectableTextAlign, 0.0f, 1.0f, "%.2f"); ImGui::SameLine(); HelpMarker("Alignment applies when a selectable is larger than its text content."); + ImGui::Text("Safe Area Padding"); ImGui::SameLine(); HelpMarker("Adjust if you cannot see the edges of your screen (e.g. on a TV where scaling has not been configured)."); + ImGui::SliderFloat2("DisplaySafeAreaPadding", (float*)&style.DisplaySafeAreaPadding, 0.0f, 30.0f, "%.0f"); + ImGui::EndTabItem(); } - ImGui::SameLine(); ImGui::PushItemWidth(120); ImGui::Combo("##output_type", &output_dest, "To Clipboard\0To TTY\0"); ImGui::PopItemWidth(); - ImGui::SameLine(); ImGui::Checkbox("Only Modified Colors", &output_only_modified); - ImGui::Text("Tip: Left-click on colored square to open color picker,\nRight-click to open edit options menu."); + if (ImGui::BeginTabItem("Colors")) + { + static int output_dest = 0; + static bool output_only_modified = true; + if (ImGui::Button("Export Unsaved")) + { + if (output_dest == 0) + ImGui::LogToClipboard(); + else + ImGui::LogToTTY(); + ImGui::LogText("ImVec4* colors = ImGui::GetStyle().Colors;" IM_NEWLINE); + for (int i = 0; i < ImGuiCol_COUNT; i++) + { + const ImVec4& col = style.Colors[i]; + const char* name = ImGui::GetStyleColorName(i); + if (!output_only_modified || memcmp(&col, &ref->Colors[i], sizeof(ImVec4)) != 0) + ImGui::LogText("colors[ImGuiCol_%s]%*s= ImVec4(%.2ff, %.2ff, %.2ff, %.2ff);" IM_NEWLINE, name, 23 - (int)strlen(name), "", col.x, col.y, col.z, col.w); + } + ImGui::LogFinish(); + } + ImGui::SameLine(); ImGui::PushItemWidth(120); ImGui::Combo("##output_type", &output_dest, "To Clipboard\0To TTY\0"); ImGui::PopItemWidth(); + ImGui::SameLine(); ImGui::Checkbox("Only Modified Colors", &output_only_modified); - static ImGuiTextFilter filter; - filter.Draw("Filter colors", 200); + static ImGuiTextFilter filter; + filter.Draw("Filter colors", ImGui::GetFontSize() * 16); - static ImGuiColorEditFlags alpha_flags = 0; - ImGui::RadioButton("Opaque", &alpha_flags, 0); ImGui::SameLine(); - ImGui::RadioButton("Alpha", &alpha_flags, ImGuiColorEditFlags_AlphaPreview); ImGui::SameLine(); - ImGui::RadioButton("Both", &alpha_flags, ImGuiColorEditFlags_AlphaPreviewHalf); + static ImGuiColorEditFlags alpha_flags = 0; + ImGui::RadioButton("Opaque", &alpha_flags, 0); ImGui::SameLine(); + ImGui::RadioButton("Alpha", &alpha_flags, ImGuiColorEditFlags_AlphaPreview); ImGui::SameLine(); + ImGui::RadioButton("Both", &alpha_flags, ImGuiColorEditFlags_AlphaPreviewHalf); ImGui::SameLine(); + HelpMarker("In the color list:\nLeft-click on colored square to open color picker,\nRight-click to open edit options menu."); - ImGui::BeginChild("#colors", ImVec2(0, 300), true, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar | ImGuiWindowFlags_NavFlattened); - ImGui::PushItemWidth(-160); - for (int i = 0; i < ImGuiCol_COUNT; i++) - { - const char* name = ImGui::GetStyleColorName(i); - if (!filter.PassFilter(name)) - continue; - ImGui::PushID(i); - ImGui::ColorEdit4("##color", (float*)&style.Colors[i], ImGuiColorEditFlags_AlphaBar | alpha_flags); - if (memcmp(&style.Colors[i], &ref->Colors[i], sizeof(ImVec4)) != 0) + ImGui::BeginChild("##colors", ImVec2(0, 0), true, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar | ImGuiWindowFlags_NavFlattened); + ImGui::PushItemWidth(-160); + for (int i = 0; i < ImGuiCol_COUNT; i++) { - // Tips: in a real user application, you may want to merge and use an icon font into the main font, so instead of "Save"/"Revert" you'd use icons. - // Read the FAQ and misc/fonts/README.txt about using icon fonts. It's really easy and super convenient! - ImGui::SameLine(0.0f, style.ItemInnerSpacing.x); if (ImGui::Button("Save")) ref->Colors[i] = style.Colors[i]; - ImGui::SameLine(0.0f, style.ItemInnerSpacing.x); if (ImGui::Button("Revert")) style.Colors[i] = ref->Colors[i]; + const char* name = ImGui::GetStyleColorName(i); + if (!filter.PassFilter(name)) + continue; + ImGui::PushID(i); + ImGui::ColorEdit4("##color", (float*)&style.Colors[i], ImGuiColorEditFlags_AlphaBar | alpha_flags); + if (memcmp(&style.Colors[i], &ref->Colors[i], sizeof(ImVec4)) != 0) + { + // Tips: in a real user application, you may want to merge and use an icon font into the main font, so instead of "Save"/"Revert" you'd use icons. + // Read the FAQ and misc/fonts/README.txt about using icon fonts. It's really easy and super convenient! + ImGui::SameLine(0.0f, style.ItemInnerSpacing.x); if (ImGui::Button("Save")) ref->Colors[i] = style.Colors[i]; + ImGui::SameLine(0.0f, style.ItemInnerSpacing.x); if (ImGui::Button("Revert")) style.Colors[i] = ref->Colors[i]; + } + ImGui::SameLine(0.0f, style.ItemInnerSpacing.x); + ImGui::TextUnformatted(name); + ImGui::PopID(); } - ImGui::SameLine(0.0f, style.ItemInnerSpacing.x); - ImGui::TextUnformatted(name); - ImGui::PopID(); - } - ImGui::PopItemWidth(); - ImGui::EndChild(); - - ImGui::TreePop(); - } + ImGui::PopItemWidth(); + ImGui::EndChild(); - bool fonts_opened = ImGui::TreeNode("Fonts", "Fonts (%d)", ImGui::GetIO().Fonts->Fonts.Size); - if (fonts_opened) - { - ImFontAtlas* atlas = ImGui::GetIO().Fonts; - if (ImGui::TreeNode("Atlas texture", "Atlas texture (%dx%d pixels)", atlas->TexWidth, atlas->TexHeight)) - { - ImGui::Image(atlas->TexID, ImVec2((float)atlas->TexWidth, (float)atlas->TexHeight), ImVec2(0,0), ImVec2(1,1), ImColor(255,255,255,255), ImColor(255,255,255,128)); - ImGui::TreePop(); + ImGui::EndTabItem(); } - ImGui::PushItemWidth(100); - for (int i = 0; i < atlas->Fonts.Size; i++) + + if (ImGui::BeginTabItem("Fonts")) { - ImFont* font = atlas->Fonts[i]; - ImGui::PushID(font); - bool font_details_opened = ImGui::TreeNode(font, "Font %d: \'%s\', %.2f px, %d glyphs", i, font->ConfigData ? font->ConfigData[0].Name : "", font->FontSize, font->Glyphs.Size); - ImGui::SameLine(); if (ImGui::SmallButton("Set as default")) ImGui::GetIO().FontDefault = font; - if (font_details_opened) + ImGuiIO& io = ImGui::GetIO(); + ImFontAtlas* atlas = io.Fonts; + HelpMarker("Read FAQ and misc/fonts/README.txt for details on font loading."); + ImGui::PushItemWidth(120); + for (int i = 0; i < atlas->Fonts.Size; i++) { - ImGui::PushFont(font); - ImGui::Text("The quick brown fox jumps over the lazy dog"); - ImGui::PopFont(); - ImGui::DragFloat("Font scale", &font->Scale, 0.005f, 0.3f, 2.0f, "%.1f"); // Scale only this font - ImGui::SameLine(); ShowHelpMarker("Note than the default embedded font is NOT meant to be scaled.\n\nFont are currently rendered into bitmaps at a given size at the time of building the atlas. You may oversample them to get some flexibility with scaling. You can also render at multiple sizes and select which one to use at runtime.\n\n(Glimmer of hope: the atlas system should hopefully be rewritten in the future to make scaling more natural and automatic.)"); - ImGui::InputFloat("Font offset", &font->DisplayOffset.y, 1, 1, "%.0f"); - ImGui::Text("Ascent: %f, Descent: %f, Height: %f", font->Ascent, font->Descent, font->Ascent - font->Descent); - ImGui::Text("Fallback character: '%c' (%d)", font->FallbackChar, font->FallbackChar); - ImGui::Text("Texture surface: %d pixels (approx) ~ %dx%d", font->MetricsTotalSurface, (int)sqrtf((float)font->MetricsTotalSurface), (int)sqrtf((float)font->MetricsTotalSurface)); - for (int config_i = 0; config_i < font->ConfigDataCount; config_i++) - if (ImFontConfig* cfg = &font->ConfigData[config_i]) - ImGui::BulletText("Input %d: \'%s\', Oversample: (%d,%d), PixelSnapH: %d", config_i, cfg->Name, cfg->OversampleH, cfg->OversampleV, cfg->PixelSnapH); - if (ImGui::TreeNode("Glyphs", "Glyphs (%d)", font->Glyphs.Size)) + ImFont* font = atlas->Fonts[i]; + ImGui::PushID(font); + bool font_details_opened = ImGui::TreeNode(font, "Font %d: \"%s\"\n%.2f px, %d glyphs, %d file(s)", i, font->ConfigData ? font->ConfigData[0].Name : "", font->FontSize, font->Glyphs.Size, font->ConfigDataCount); + ImGui::SameLine(); if (ImGui::SmallButton("Set as default")) { io.FontDefault = font; } + if (font_details_opened) { - // Display all glyphs of the fonts in separate pages of 256 characters - for (int base = 0; base < 0x10000; base += 256) + ImGui::PushFont(font); + ImGui::Text("The quick brown fox jumps over the lazy dog"); + ImGui::PopFont(); + ImGui::DragFloat("Font scale", &font->Scale, 0.005f, 0.3f, 2.0f, "%.1f"); // Scale only this font + ImGui::SameLine(); HelpMarker("Note than the default embedded font is NOT meant to be scaled.\n\nFont are currently rendered into bitmaps at a given size at the time of building the atlas. You may oversample them to get some flexibility with scaling. You can also render at multiple sizes and select which one to use at runtime.\n\n(Glimmer of hope: the atlas system should hopefully be rewritten in the future to make scaling more natural and automatic.)"); + ImGui::InputFloat("Font offset", &font->DisplayOffset.y, 1, 1, "%.0f"); + ImGui::Text("Ascent: %f, Descent: %f, Height: %f", font->Ascent, font->Descent, font->Ascent - font->Descent); + ImGui::Text("Fallback character: '%c' (%d)", font->FallbackChar, font->FallbackChar); + const float surface_sqrt = sqrtf((float)font->MetricsTotalSurface); + ImGui::Text("Texture surface: %d pixels (approx) ~ %dx%d", font->MetricsTotalSurface, (int)surface_sqrt, (int)surface_sqrt); + for (int config_i = 0; config_i < font->ConfigDataCount; config_i++) + if (const ImFontConfig* cfg = &font->ConfigData[config_i]) + ImGui::BulletText("Input %d: \'%s\', Oversample: (%d,%d), PixelSnapH: %d", config_i, cfg->Name, cfg->OversampleH, cfg->OversampleV, cfg->PixelSnapH); + if (ImGui::TreeNode("Glyphs", "Glyphs (%d)", font->Glyphs.Size)) { - int count = 0; - for (int n = 0; n < 256; n++) - count += font->FindGlyphNoFallback((ImWchar)(base + n)) ? 1 : 0; - if (count > 0 && ImGui::TreeNode((void*)(intptr_t)base, "U+%04X..U+%04X (%d %s)", base, base+255, count, count > 1 ? "glyphs" : "glyph")) + // Display all glyphs of the fonts in separate pages of 256 characters + for (int base = 0; base < 0x10000; base += 256) { - float cell_size = font->FontSize * 1; - float cell_spacing = style.ItemSpacing.y; - ImVec2 base_pos = ImGui::GetCursorScreenPos(); - ImDrawList* draw_list = ImGui::GetWindowDrawList(); + int count = 0; for (int n = 0; n < 256; n++) + count += font->FindGlyphNoFallback((ImWchar)(base + n)) ? 1 : 0; + if (count > 0 && ImGui::TreeNode((void*)(intptr_t)base, "U+%04X..U+%04X (%d %s)", base, base + 255, count, count > 1 ? "glyphs" : "glyph")) { - ImVec2 cell_p1(base_pos.x + (n % 16) * (cell_size + cell_spacing), base_pos.y + (n / 16) * (cell_size + cell_spacing)); - ImVec2 cell_p2(cell_p1.x + cell_size, cell_p1.y + cell_size); - const ImFontGlyph* glyph = font->FindGlyphNoFallback((ImWchar)(base+n)); - draw_list->AddRect(cell_p1, cell_p2, glyph ? IM_COL32(255,255,255,100) : IM_COL32(255,255,255,50)); - if (glyph) - font->RenderChar(draw_list, cell_size, cell_p1, ImGui::GetColorU32(ImGuiCol_Text), (ImWchar)(base+n)); // We use ImFont::RenderChar as a shortcut because we don't have UTF-8 conversion functions available to generate a string. - if (glyph && ImGui::IsMouseHoveringRect(cell_p1, cell_p2)) + float cell_size = font->FontSize * 1; + float cell_spacing = style.ItemSpacing.y; + ImVec2 base_pos = ImGui::GetCursorScreenPos(); + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + for (int n = 0; n < 256; n++) { - ImGui::BeginTooltip(); - ImGui::Text("Codepoint: U+%04X", base+n); - ImGui::Separator(); - ImGui::Text("AdvanceX: %.1f", glyph->AdvanceX); - ImGui::Text("Pos: (%.2f,%.2f)->(%.2f,%.2f)", glyph->X0, glyph->Y0, glyph->X1, glyph->Y1); - ImGui::Text("UV: (%.3f,%.3f)->(%.3f,%.3f)", glyph->U0, glyph->V0, glyph->U1, glyph->V1); - ImGui::EndTooltip(); + ImVec2 cell_p1(base_pos.x + (n % 16) * (cell_size + cell_spacing), base_pos.y + (n / 16) * (cell_size + cell_spacing)); + ImVec2 cell_p2(cell_p1.x + cell_size, cell_p1.y + cell_size); + const ImFontGlyph* glyph = font->FindGlyphNoFallback((ImWchar)(base + n)); + draw_list->AddRect(cell_p1, cell_p2, glyph ? IM_COL32(255, 255, 255, 100) : IM_COL32(255, 255, 255, 50)); + if (glyph) + font->RenderChar(draw_list, cell_size, cell_p1, ImGui::GetColorU32(ImGuiCol_Text), (ImWchar)(base + n)); // We use ImFont::RenderChar as a shortcut because we don't have UTF-8 conversion functions available to generate a string. + if (glyph && ImGui::IsMouseHoveringRect(cell_p1, cell_p2)) + { + ImGui::BeginTooltip(); + ImGui::Text("Codepoint: U+%04X", base + n); + ImGui::Separator(); + ImGui::Text("AdvanceX: %.1f", glyph->AdvanceX); + ImGui::Text("Pos: (%.2f,%.2f)->(%.2f,%.2f)", glyph->X0, glyph->Y0, glyph->X1, glyph->Y1); + ImGui::Text("UV: (%.3f,%.3f)->(%.3f,%.3f)", glyph->U0, glyph->V0, glyph->U1, glyph->V1); + ImGui::EndTooltip(); + } } + ImGui::Dummy(ImVec2((cell_size + cell_spacing) * 16, (cell_size + cell_spacing) * 16)); + ImGui::TreePop(); } - ImGui::Dummy(ImVec2((cell_size + cell_spacing) * 16, (cell_size + cell_spacing) * 16)); - ImGui::TreePop(); } + ImGui::TreePop(); } ImGui::TreePop(); } + ImGui::PopID(); + } + if (ImGui::TreeNode("Atlas texture", "Atlas texture (%dx%d pixels)", atlas->TexWidth, atlas->TexHeight)) + { + ImGui::Image(atlas->TexID, ImVec2((float)atlas->TexWidth, (float)atlas->TexHeight), ImVec2(0, 0), ImVec2(1, 1), ImColor(255, 255, 255, 255), ImColor(255, 255, 255, 128)); ImGui::TreePop(); } - ImGui::PopID(); + + static float window_scale = 1.0f; + if (ImGui::DragFloat("this window scale", &window_scale, 0.005f, 0.3f, 2.0f, "%.2f")) // scale only this window + ImGui::SetWindowFontScale(window_scale); + ImGui::DragFloat("global scale", &io.FontGlobalScale, 0.005f, 0.3f, 2.0f, "%.2f"); // scale everything + ImGui::PopItemWidth(); + + ImGui::EndTabItem(); } - static float window_scale = 1.0f; - ImGui::DragFloat("this window scale", &window_scale, 0.005f, 0.3f, 2.0f, "%.1f"); // scale only this window - ImGui::DragFloat("global scale", &ImGui::GetIO().FontGlobalScale, 0.005f, 0.3f, 2.0f, "%.1f"); // scale everything - ImGui::PopItemWidth(); - ImGui::SetWindowFontScale(window_scale); - ImGui::TreePop(); + + if (ImGui::BeginTabItem("Rendering")) + { + ImGui::Checkbox("Anti-aliased lines", &style.AntiAliasedLines); ImGui::SameLine(); HelpMarker("When disabling anti-aliasing lines, you'll probably want to disable borders in your style as well."); + ImGui::Checkbox("Anti-aliased fill", &style.AntiAliasedFill); + ImGui::PushItemWidth(100); + ImGui::DragFloat("Curve Tessellation Tolerance", &style.CurveTessellationTol, 0.02f, 0.10f, FLT_MAX, "%.2f", 2.0f); + if (style.CurveTessellationTol < 0.10f) style.CurveTessellationTol = 0.10f; + ImGui::DragFloat("Global Alpha", &style.Alpha, 0.005f, 0.20f, 1.0f, "%.2f"); // Not exposing zero here so user doesn't "lose" the UI (zero alpha clips all widgets). But application code could have a toggle to switch between zero and non-zero. + ImGui::PopItemWidth(); + + ImGui::EndTabItem(); + } + + ImGui::EndTabBar(); } ImGui::PopItemWidth(); @@ -2825,10 +3198,12 @@ struct ExampleAppConsole { char InputBuf[256]; ImVector Items; - bool ScrollToBottom; + ImVector Commands; ImVector History; int HistoryPos; // -1: new line, 0..History.Size-1 browsing history. - ImVector Commands; + ImGuiTextFilter Filter; + bool AutoScroll; + bool ScrollToBottom; ExampleAppConsole() { @@ -2839,6 +3214,8 @@ struct ExampleAppConsole Commands.push_back("HISTORY"); Commands.push_back("CLEAR"); Commands.push_back("CLASSIFY"); // "classify" is only here to provide an example of "C"+[tab] completing to "CL" and displaying matches. + AutoScroll = true; + ScrollToBottom = true; AddLog("Welcome to Dear ImGui!"); } ~ExampleAppConsole() @@ -2851,7 +3228,7 @@ struct ExampleAppConsole // Portable helpers static int Stricmp(const char* str1, const char* str2) { int d; while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; } return d; } static int Strnicmp(const char* str1, const char* str2, int n) { int d = 0; while (n > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; n--; } return d; } - static char* Strdup(const char *str) { size_t len = strlen(str) + 1; void* buff = malloc(len); return (char*)memcpy(buff, (const void*)str, len); } + static char* Strdup(const char *str) { size_t len = strlen(str) + 1; void* buf = malloc(len); IM_ASSERT(buf); return (char*)memcpy(buf, (const void*)str, len); } static void Strtrim(char* str) { char* str_end = str + strlen(str); while (str_end > str && str_end[-1] == ' ') str_end--; *str_end = 0; } void ClearLog() @@ -2872,7 +3249,8 @@ struct ExampleAppConsole buf[IM_ARRAYSIZE(buf)-1] = 0; va_end(args); Items.push_back(Strdup(buf)); - ScrollToBottom = true; + if (AutoScroll) + ScrollToBottom = true; } void Draw(const char* title, bool* p_open) @@ -2898,7 +3276,7 @@ struct ExampleAppConsole // TODO: display items starting from the bottom - if (ImGui::SmallButton("Add Dummy Text")) { AddLog("%d some text", Items.Size); AddLog("some more text"); AddLog("display very important message here!"); } ImGui::SameLine(); + if (ImGui::SmallButton("Add Dummy Text")) { AddLog("%d some text", Items.Size); AddLog("some more text"); AddLog("display very important message here!"); } ImGui::SameLine(); if (ImGui::SmallButton("Add Dummy Error")) { AddLog("[error] something went wrong"); } ImGui::SameLine(); if (ImGui::SmallButton("Clear")) { ClearLog(); } ImGui::SameLine(); bool copy_to_clipboard = ImGui::SmallButton("Copy"); ImGui::SameLine(); @@ -2907,10 +3285,20 @@ struct ExampleAppConsole ImGui::Separator(); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0,0)); - static ImGuiTextFilter filter; - filter.Draw("Filter (\"incl,-excl\") (\"error\")", 180); - ImGui::PopStyleVar(); + // Options menu + if (ImGui::BeginPopup("Options")) + { + if (ImGui::Checkbox("Auto-scroll", &AutoScroll)) + if (AutoScroll) + ScrollToBottom = true; + ImGui::EndPopup(); + } + + // Options, Filter + if (ImGui::Button("Options")) + ImGui::OpenPopup("Options"); + ImGui::SameLine(); + Filter.Draw("Filter (\"incl,-excl\") (\"error\")", 180); ImGui::Separator(); const float footer_height_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing(); // 1 separator, 1 input text @@ -2935,18 +3323,19 @@ struct ExampleAppConsole ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4,1)); // Tighten spacing if (copy_to_clipboard) ImGui::LogToClipboard(); - ImVec4 col_default_text = ImGui::GetStyleColorVec4(ImGuiCol_Text); for (int i = 0; i < Items.Size; i++) { const char* item = Items[i]; - if (!filter.PassFilter(item)) + if (!Filter.PassFilter(item)) continue; - ImVec4 col = col_default_text; - if (strstr(item, "[error]")) col = ImColor(1.0f,0.4f,0.4f,1.0f); - else if (strncmp(item, "# ", 2) == 0) col = ImColor(1.0f,0.78f,0.58f,1.0f); - ImGui::PushStyleColor(ImGuiCol_Text, col); + + // Normally you would store more information in your item (e.g. make Items[] an array of structure, store color/type etc.) + bool pop_color = false; + if (strstr(item, "[error]")) { ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.4f, 0.4f, 1.0f)); pop_color = true; } + else if (strncmp(item, "# ", 2) == 0) { ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.8f, 0.6f, 1.0f)); pop_color = true; } ImGui::TextUnformatted(item); - ImGui::PopStyleColor(); + if (pop_color) + ImGui::PopStyleColor(); } if (copy_to_clipboard) ImGui::LogFinish(); @@ -3013,6 +3402,9 @@ struct ExampleAppConsole { AddLog("Unknown command: '%s'\n", command_line); } + + // On commad input, we scroll to bottom even if AutoScroll==false + ScrollToBottom = true; } static int TextEditCallbackStub(ImGuiInputTextCallbackData* data) // In C++11 you are better off using lambdas for this sort of forwarding callbacks @@ -3140,10 +3532,23 @@ struct ExampleAppLog { ImGuiTextBuffer Buf; ImGuiTextFilter Filter; - ImVector LineOffsets; // Index to lines offset + ImVector LineOffsets; // Index to lines offset. We maintain this with AddLog() calls, allowing us to have a random access on lines + bool AutoScroll; bool ScrollToBottom; - void Clear() { Buf.clear(); LineOffsets.clear(); } + ExampleAppLog() + { + AutoScroll = true; + ScrollToBottom = false; + Clear(); + } + + void Clear() + { + Buf.clear(); + LineOffsets.clear(); + LineOffsets.push_back(0); + } void AddLog(const char* fmt, ...) IM_FMTARGS(2) { @@ -3154,43 +3559,88 @@ struct ExampleAppLog va_end(args); for (int new_size = Buf.size(); old_size < new_size; old_size++) if (Buf[old_size] == '\n') - LineOffsets.push_back(old_size); - ScrollToBottom = true; + LineOffsets.push_back(old_size + 1); + if (AutoScroll) + ScrollToBottom = true; } void Draw(const char* title, bool* p_open = NULL) { - ImGui::SetNextWindowSize(ImVec2(500,400), ImGuiCond_FirstUseEver); if (!ImGui::Begin(title, p_open)) { ImGui::End(); return; } - if (ImGui::Button("Clear")) Clear(); + + // Options menu + if (ImGui::BeginPopup("Options")) + { + if (ImGui::Checkbox("Auto-scroll", &AutoScroll)) + if (AutoScroll) + ScrollToBottom = true; + ImGui::EndPopup(); + } + + // Main window + if (ImGui::Button("Options")) + ImGui::OpenPopup("Options"); + ImGui::SameLine(); + bool clear = ImGui::Button("Clear"); ImGui::SameLine(); bool copy = ImGui::Button("Copy"); ImGui::SameLine(); Filter.Draw("Filter", -100.0f); + ImGui::Separator(); ImGui::BeginChild("scrolling", ImVec2(0,0), false, ImGuiWindowFlags_HorizontalScrollbar); - if (copy) ImGui::LogToClipboard(); + if (clear) + Clear(); + if (copy) + ImGui::LogToClipboard(); + + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); + const char* buf = Buf.begin(); + const char* buf_end = Buf.end(); if (Filter.IsActive()) { - const char* buf_begin = Buf.begin(); - const char* line = buf_begin; - for (int line_no = 0; line != NULL; line_no++) + // In this example we don't use the clipper when Filter is enabled. + // This is because we don't have a random access on the result on our filter. + // A real application processing logs with ten of thousands of entries may want to store the result of search/filter. + // especially if the filtering function is not trivial (e.g. reg-exp). + for (int line_no = 0; line_no < LineOffsets.Size; line_no++) { - const char* line_end = (line_no < LineOffsets.Size) ? buf_begin + LineOffsets[line_no] : NULL; - if (Filter.PassFilter(line, line_end)) - ImGui::TextUnformatted(line, line_end); - line = line_end && line_end[1] ? line_end + 1 : NULL; + const char* line_start = buf + LineOffsets[line_no]; + const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end; + if (Filter.PassFilter(line_start, line_end)) + ImGui::TextUnformatted(line_start, line_end); } } else { - ImGui::TextUnformatted(Buf.begin()); + // The simplest and easy way to display the entire buffer: + // ImGui::TextUnformatted(buf_begin, buf_end); + // And it'll just work. TextUnformatted() has specialization for large blob of text and will fast-forward to skip non-visible lines. + // Here we instead demonstrate using the clipper to only process lines that are within the visible area. + // If you have tens of thousands of items and their processing cost is non-negligible, coarse clipping them on your side is recommended. + // Using ImGuiListClipper requires A) random access into your data, and B) items all being the same height, + // both of which we can handle since we an array pointing to the beginning of each line of text. + // When using the filter (in the block of code above) we don't have random access into the data to display anymore, which is why we don't use the clipper. + // Storing or skimming through the search result would make it possible (and would be recommended if you want to search through tens of thousands of entries) + ImGuiListClipper clipper; + clipper.Begin(LineOffsets.Size); + while (clipper.Step()) + { + for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++) + { + const char* line_start = buf + LineOffsets[line_no]; + const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end; + ImGui::TextUnformatted(line_start, line_end); + } + } + clipper.End(); } + ImGui::PopStyleVar(); if (ScrollToBottom) ImGui::SetScrollHereY(1.0f); @@ -3205,15 +3655,24 @@ static void ShowExampleAppLog(bool* p_open) { static ExampleAppLog log; - // Demo: add random items (unless Ctrl is held) - static double last_time = -1.0; - double time = ImGui::GetTime(); - if (time - last_time >= 0.20f && !ImGui::GetIO().KeyCtrl) + // For the demo: add a debug button _BEFORE_ the normal log window contents + // We take advantage of the fact that multiple calls to Begin()/End() are appending to the same window. + // Most of the contents of the window will be added by the log.Draw() call. + ImGui::SetNextWindowSize(ImVec2(500, 400), ImGuiCond_FirstUseEver); + ImGui::Begin("Example: Log", p_open); + if (ImGui::SmallButton("[Debug] Add 5 entries")) { - const char* random_words[] = { "system", "info", "warning", "error", "fatal", "notice", "log" }; - log.AddLog("[%s] Hello, time is %.1f, frame count is %d\n", random_words[rand() % IM_ARRAYSIZE(random_words)], time, ImGui::GetFrameCount()); - last_time = time; + static int counter = 0; + for (int n = 0; n < 5; n++) + { + const char* categories[3] = { "info", "warn", "error" }; + const char* words[] = { "Bumfuzzled", "Cattywampus", "Snickersnee", "Abibliophobia", "Absquatulate", "Nincompoop", "Pauciloquent" }; + log.AddLog("[%05d] [%s] Hello, current time is %.1f, here's a word: '%s'\n", + ImGui::GetFrameCount(), categories[counter % IM_ARRAYSIZE(categories)], ImGui::GetTime(), words[counter % IM_ARRAYSIZE(words)]); + counter++; + } } + ImGui::End(); log.Draw("Example: Log", p_open); } @@ -3226,7 +3685,7 @@ static void ShowExampleAppLog(bool* p_open) static void ShowExampleAppLayout(bool* p_open) { ImGui::SetNextWindowSize(ImVec2(500, 440), ImGuiCond_FirstUseEver); - if (ImGui::Begin("Example: Layout", p_open, ImGuiWindowFlags_MenuBar)) + if (ImGui::Begin("Example: Simple layout", p_open, ImGuiWindowFlags_MenuBar)) { if (ImGui::BeginMenuBar()) { @@ -3256,7 +3715,20 @@ static void ShowExampleAppLayout(bool* p_open) ImGui::BeginChild("item view", ImVec2(0, -ImGui::GetFrameHeightWithSpacing())); // Leave room for 1 line below us ImGui::Text("MyObject: %d", selected); ImGui::Separator(); - ImGui::TextWrapped("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. "); + if (ImGui::BeginTabBar("##Tabs", ImGuiTabBarFlags_None)) + { + if (ImGui::BeginTabItem("Description")) + { + ImGui::TextWrapped("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. "); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Details")) + { + ImGui::Text("ID: 0123456789"); + ImGui::EndTabItem(); + } + ImGui::EndTabBar(); + } ImGui::EndChild(); if (ImGui::Button("Revert")) {} ImGui::SameLine(); @@ -3280,7 +3752,7 @@ static void ShowExampleAppPropertyEditor(bool* p_open) return; } - ShowHelpMarker("This example shows how you may implement a property editor using two columns.\nAll objects/fields data are dummies here.\nRemember that in many simple cases, you can use ImGui::SameLine(xxx) to position\nyour cursor horizontally instead of using the Columns() API."); + HelpMarker("This example shows how you may implement a property editor using two columns.\nAll objects/fields data are dummies here.\nRemember that in many simple cases, you can use ImGui::SameLine(xxx) to position\nyour cursor horizontally instead of using the Columns() API."); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2,2)); ImGui::Columns(2); @@ -3439,8 +3911,8 @@ static void ShowExampleAppConstrainedResize(bool* p_open) if (type == 2) ImGui::SetNextWindowSizeConstraints(ImVec2(100, 100), ImVec2(FLT_MAX, FLT_MAX)); // Width > 100, Height > 100 if (type == 3) ImGui::SetNextWindowSizeConstraints(ImVec2(400, -1), ImVec2(500, -1)); // Width 400-500 if (type == 4) ImGui::SetNextWindowSizeConstraints(ImVec2(-1, 400), ImVec2(-1, 500)); // Height 400-500 - if (type == 5) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Square); // Always Square - if (type == 6) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Step, (void*)100);// Fixed Step + if (type == 5) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Square); // Always Square + if (type == 6) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Step, (void*)(intptr_t)100); // Fixed Step ImGuiWindowFlags flags = auto_resize ? ImGuiWindowFlags_AlwaysAutoResize : 0; if (ImGui::Begin("Example: Constrained Resize", p_open, flags)) @@ -3478,17 +3950,20 @@ static void ShowExampleAppSimpleOverlay(bool* p_open) { const float DISTANCE = 10.0f; static int corner = 0; - ImVec2 window_pos = ImVec2((corner & 1) ? ImGui::GetIO().DisplaySize.x - DISTANCE : DISTANCE, (corner & 2) ? ImGui::GetIO().DisplaySize.y - DISTANCE : DISTANCE); - ImVec2 window_pos_pivot = ImVec2((corner & 1) ? 1.0f : 0.0f, (corner & 2) ? 1.0f : 0.0f); + ImGuiIO& io = ImGui::GetIO(); if (corner != -1) + { + ImVec2 window_pos = ImVec2((corner & 1) ? io.DisplaySize.x - DISTANCE : DISTANCE, (corner & 2) ? io.DisplaySize.y - DISTANCE : DISTANCE); + ImVec2 window_pos_pivot = ImVec2((corner & 1) ? 1.0f : 0.0f, (corner & 2) ? 1.0f : 0.0f); ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot); + } ImGui::SetNextWindowBgAlpha(0.3f); // Transparent background - if (ImGui::Begin("Example: Simple Overlay", p_open, (corner != -1 ? ImGuiWindowFlags_NoMove : 0) | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav)) + if (ImGui::Begin("Example: Simple overlay", p_open, (corner != -1 ? ImGuiWindowFlags_NoMove : 0) | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav)) { ImGui::Text("Simple overlay\n" "in the corner of the screen.\n" "(right-click to change position)"); ImGui::Separator(); if (ImGui::IsMousePosValid()) - ImGui::Text("Mouse Position: (%.1f,%.1f)", ImGui::GetIO().MousePos.x, ImGui::GetIO().MousePos.y); + ImGui::Text("Mouse Position: (%.1f,%.1f)", io.MousePos.x, io.MousePos.y); else ImGui::Text("Mouse Position: "); if (ImGui::BeginPopupContextWindow()) @@ -3556,100 +4031,398 @@ static void ShowExampleAppCustomRendering(bool* p_open) // In this example we are not using the maths operators! ImDrawList* draw_list = ImGui::GetWindowDrawList(); - // Primitives - ImGui::Text("Primitives"); - static float sz = 36.0f; - static float thickness = 4.0f; - static ImVec4 col = ImVec4(1.0f, 1.0f, 0.4f, 1.0f); - ImGui::DragFloat("Size", &sz, 0.2f, 2.0f, 72.0f, "%.0f"); - ImGui::DragFloat("Thickness", &thickness, 0.05f, 1.0f, 8.0f, "%.02f"); - ImGui::ColorEdit3("Color", &col.x); - { - const ImVec2 p = ImGui::GetCursorScreenPos(); - const ImU32 col32 = ImColor(col); - float x = p.x + 4.0f, y = p.y + 4.0f, spacing = 8.0f; - for (int n = 0; n < 2; n++) - { - float curr_thickness = (n == 0) ? 1.0f : thickness; - draw_list->AddCircle(ImVec2(x+sz*0.5f, y+sz*0.5f), sz*0.5f, col32, 20, curr_thickness); x += sz+spacing; - draw_list->AddRect(ImVec2(x, y), ImVec2(x+sz, y+sz), col32, 0.0f, ImDrawCornerFlags_All, curr_thickness); x += sz+spacing; - draw_list->AddRect(ImVec2(x, y), ImVec2(x+sz, y+sz), col32, 10.0f, ImDrawCornerFlags_All, curr_thickness); x += sz+spacing; - draw_list->AddRect(ImVec2(x, y), ImVec2(x+sz, y+sz), col32, 10.0f, ImDrawCornerFlags_TopLeft|ImDrawCornerFlags_BotRight, curr_thickness); x += sz+spacing; - draw_list->AddTriangle(ImVec2(x+sz*0.5f, y), ImVec2(x+sz,y+sz-0.5f), ImVec2(x,y+sz-0.5f), col32, curr_thickness); x += sz+spacing; - draw_list->AddLine(ImVec2(x, y), ImVec2(x+sz, y ), col32, curr_thickness); x += sz+spacing; // Horizontal line (note: drawing a filled rectangle will be faster!) - draw_list->AddLine(ImVec2(x, y), ImVec2(x, y+sz), col32, curr_thickness); x += spacing; // Vertical line (note: drawing a filled rectangle will be faster!) - draw_list->AddLine(ImVec2(x, y), ImVec2(x+sz, y+sz), col32, curr_thickness); x += sz+spacing; // Diagonal line - draw_list->AddBezierCurve(ImVec2(x, y), ImVec2(x+sz*1.3f,y+sz*0.3f), ImVec2(x+sz-sz*1.3f,y+sz-sz*0.3f), ImVec2(x+sz, y+sz), col32, curr_thickness); - x = p.x + 4; - y += sz+spacing; - } - draw_list->AddCircleFilled(ImVec2(x+sz*0.5f, y+sz*0.5f), sz*0.5f, col32, 32); x += sz+spacing; - draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x+sz, y+sz), col32); x += sz+spacing; - draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x+sz, y+sz), col32, 10.0f); x += sz+spacing; - draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x+sz, y+sz), col32, 10.0f, ImDrawCornerFlags_TopLeft|ImDrawCornerFlags_BotRight); x += sz+spacing; - draw_list->AddTriangleFilled(ImVec2(x+sz*0.5f, y), ImVec2(x+sz,y+sz-0.5f), ImVec2(x,y+sz-0.5f), col32); x += sz+spacing; - draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x+sz, y+thickness), col32); x += sz+spacing; // Horizontal line (faster than AddLine, but only handle integer thickness) - draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x+thickness, y+sz), col32); x += spacing+spacing; // Vertical line (faster than AddLine, but only handle integer thickness) - draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x+1, y+1), col32); x += sz; // Pixel (faster than AddLine) - draw_list->AddRectFilledMultiColor(ImVec2(x, y), ImVec2(x+sz, y+sz), IM_COL32(0,0,0,255), IM_COL32(255,0,0,255), IM_COL32(255,255,0,255), IM_COL32(0,255,0,255)); - ImGui::Dummy(ImVec2((sz+spacing)*8, (sz+spacing)*3)); - } - ImGui::Separator(); + if (ImGui::BeginTabBar("##TabBar")) { - static ImVector points; - static bool adding_line = false; - ImGui::Text("Canvas example"); - if (ImGui::Button("Clear")) points.clear(); - if (points.Size >= 2) { ImGui::SameLine(); if (ImGui::Button("Undo")) { points.pop_back(); points.pop_back(); } } - ImGui::Text("Left-click and drag to add lines,\nRight-click to undo"); - - // Here we are using InvisibleButton() as a convenience to 1) advance the cursor and 2) allows us to use IsItemHovered() - // But you can also draw directly and poll mouse/keyboard by yourself. You can manipulate the cursor using GetCursorPos() and SetCursorPos(). - // If you only use the ImDrawList API, you can notify the owner window of its extends by using SetCursorPos(max). - ImVec2 canvas_pos = ImGui::GetCursorScreenPos(); // ImDrawList API uses screen coordinates! - ImVec2 canvas_size = ImGui::GetContentRegionAvail(); // Resize canvas to what's available - if (canvas_size.x < 50.0f) canvas_size.x = 50.0f; - if (canvas_size.y < 50.0f) canvas_size.y = 50.0f; - draw_list->AddRectFilledMultiColor(canvas_pos, ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y), IM_COL32(50, 50, 50, 255), IM_COL32(50, 50, 60, 255), IM_COL32(60, 60, 70, 255), IM_COL32(50, 50, 60, 255)); - draw_list->AddRect(canvas_pos, ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y), IM_COL32(255, 255, 255, 255)); - - bool adding_preview = false; - ImGui::InvisibleButton("canvas", canvas_size); - ImVec2 mouse_pos_in_canvas = ImVec2(ImGui::GetIO().MousePos.x - canvas_pos.x, ImGui::GetIO().MousePos.y - canvas_pos.y); - if (adding_line) - { - adding_preview = true; - points.push_back(mouse_pos_in_canvas); - if (!ImGui::IsMouseDown(0)) - adding_line = adding_preview = false; + // Primitives + if (ImGui::BeginTabItem("Primitives")) + { + static float sz = 36.0f; + static float thickness = 4.0f; + static ImVec4 col = ImVec4(1.0f, 1.0f, 0.4f, 1.0f); + ImGui::DragFloat("Size", &sz, 0.2f, 2.0f, 72.0f, "%.0f"); + ImGui::DragFloat("Thickness", &thickness, 0.05f, 1.0f, 8.0f, "%.02f"); + ImGui::ColorEdit4("Color", &col.x); + const ImVec2 p = ImGui::GetCursorScreenPos(); + const ImU32 col32 = ImColor(col); + float x = p.x + 4.0f, y = p.y + 4.0f, spacing = 8.0f; + for (int n = 0; n < 2; n++) + { + // First line uses a thickness of 1.0, second line uses the configurable thickness + float th = (n == 0) ? 1.0f : thickness; + draw_list->AddCircle(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col32, 6, th); x += sz + spacing; // Hexagon + draw_list->AddCircle(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col32, 20, th); x += sz + spacing; // Circle + draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col32, 0.0f, ImDrawCornerFlags_All, th); x += sz + spacing; + draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col32, 10.0f, ImDrawCornerFlags_All, th); x += sz + spacing; + draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col32, 10.0f, ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_BotRight, th); x += sz + spacing; + draw_list->AddTriangle(ImVec2(x + sz*0.5f, y), ImVec2(x + sz, y + sz - 0.5f), ImVec2(x, y + sz - 0.5f), col32, th); x += sz + spacing; + draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y), col32, th); x += sz + spacing; // Horizontal line (note: drawing a filled rectangle will be faster!) + draw_list->AddLine(ImVec2(x, y), ImVec2(x, y + sz), col32, th); x += spacing; // Vertical line (note: drawing a filled rectangle will be faster!) + draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y + sz), col32, th); x += sz + spacing; // Diagonal line + draw_list->AddBezierCurve(ImVec2(x, y), ImVec2(x + sz*1.3f, y + sz*0.3f), ImVec2(x + sz - sz*1.3f, y + sz - sz*0.3f), ImVec2(x + sz, y + sz), col32, th); + x = p.x + 4; + y += sz + spacing; + } + draw_list->AddCircleFilled(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col32, 6); x += sz + spacing; // Hexagon + draw_list->AddCircleFilled(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col32, 32); x += sz + spacing; // Circle + draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col32); x += sz + spacing; + draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col32, 10.0f); x += sz + spacing; + draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col32, 10.0f, ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_BotRight); x += sz + spacing; + draw_list->AddTriangleFilled(ImVec2(x + sz*0.5f, y), ImVec2(x + sz, y + sz - 0.5f), ImVec2(x, y + sz - 0.5f), col32); x += sz + spacing; + draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + thickness), col32); x += sz + spacing; // Horizontal line (faster than AddLine, but only handle integer thickness) + draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + thickness, y + sz), col32); x += spacing + spacing; // Vertical line (faster than AddLine, but only handle integer thickness) + draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + 1, y + 1), col32); x += sz; // Pixel (faster than AddLine) + draw_list->AddRectFilledMultiColor(ImVec2(x, y), ImVec2(x + sz, y + sz), IM_COL32(0, 0, 0, 255), IM_COL32(255, 0, 0, 255), IM_COL32(255, 255, 0, 255), IM_COL32(0, 255, 0, 255)); + ImGui::Dummy(ImVec2((sz + spacing) * 9.5f, (sz + spacing) * 3)); + ImGui::EndTabItem(); } - if (ImGui::IsItemHovered()) + + if (ImGui::BeginTabItem("Canvas")) { - if (!adding_line && ImGui::IsMouseClicked(0)) + static ImVector points; + static bool adding_line = false; + if (ImGui::Button("Clear")) points.clear(); + if (points.Size >= 2) { ImGui::SameLine(); if (ImGui::Button("Undo")) { points.pop_back(); points.pop_back(); } } + ImGui::Text("Left-click and drag to add lines,\nRight-click to undo"); + + // Here we are using InvisibleButton() as a convenience to 1) advance the cursor and 2) allows us to use IsItemHovered() + // But you can also draw directly and poll mouse/keyboard by yourself. You can manipulate the cursor using GetCursorPos() and SetCursorPos(). + // If you only use the ImDrawList API, you can notify the owner window of its extends by using SetCursorPos(max). + ImVec2 canvas_pos = ImGui::GetCursorScreenPos(); // ImDrawList API uses screen coordinates! + ImVec2 canvas_size = ImGui::GetContentRegionAvail(); // Resize canvas to what's available + if (canvas_size.x < 50.0f) canvas_size.x = 50.0f; + if (canvas_size.y < 50.0f) canvas_size.y = 50.0f; + draw_list->AddRectFilledMultiColor(canvas_pos, ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y), IM_COL32(50, 50, 50, 255), IM_COL32(50, 50, 60, 255), IM_COL32(60, 60, 70, 255), IM_COL32(50, 50, 60, 255)); + draw_list->AddRect(canvas_pos, ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y), IM_COL32(255, 255, 255, 255)); + + bool adding_preview = false; + ImGui::InvisibleButton("canvas", canvas_size); + ImVec2 mouse_pos_in_canvas = ImVec2(ImGui::GetIO().MousePos.x - canvas_pos.x, ImGui::GetIO().MousePos.y - canvas_pos.y); + if (adding_line) { + adding_preview = true; points.push_back(mouse_pos_in_canvas); - adding_line = true; + if (!ImGui::IsMouseDown(0)) + adding_line = adding_preview = false; } - if (ImGui::IsMouseClicked(1) && !points.empty()) + if (ImGui::IsItemHovered()) { - adding_line = adding_preview = false; - points.pop_back(); + if (!adding_line && ImGui::IsMouseClicked(0)) + { + points.push_back(mouse_pos_in_canvas); + adding_line = true; + } + if (ImGui::IsMouseClicked(1) && !points.empty()) + { + adding_line = adding_preview = false; + points.pop_back(); + points.pop_back(); + } + } + draw_list->PushClipRect(canvas_pos, ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y), true); // clip lines within the canvas (if we resize it, etc.) + for (int i = 0; i < points.Size - 1; i += 2) + draw_list->AddLine(ImVec2(canvas_pos.x + points[i].x, canvas_pos.y + points[i].y), ImVec2(canvas_pos.x + points[i + 1].x, canvas_pos.y + points[i + 1].y), IM_COL32(255, 255, 0, 255), 2.0f); + draw_list->PopClipRect(); + if (adding_preview) points.pop_back(); + ImGui::EndTabItem(); + } + + if (ImGui::BeginTabItem("BG/FG draw lists")) + { + static bool draw_bg = true; + static bool draw_fg = true; + ImGui::Checkbox("Draw in Background draw list", &draw_bg); + ImGui::Checkbox("Draw in Foreground draw list", &draw_fg); + ImVec2 window_pos = ImGui::GetWindowPos(); + ImVec2 window_size = ImGui::GetWindowSize(); + ImVec2 window_center = ImVec2(window_pos.x + window_size.x * 0.5f, window_pos.y + window_size.y * 0.5f); + if (draw_bg) + ImGui::GetBackgroundDrawList()->AddCircle(window_center, window_size.x * 0.6f, IM_COL32(255, 0, 0, 200), 32, 10); + if (draw_fg) + ImGui::GetForegroundDrawList()->AddCircle(window_center, window_size.y * 0.6f, IM_COL32(0, 255, 0, 200), 32, 10); + ImGui::EndTabItem(); + } + + ImGui::EndTabBar(); + } + + ImGui::End(); +} + +//----------------------------------------------------------------------------- +// [SECTION] Example App: Documents Handling / ShowExampleAppDocuments() +//----------------------------------------------------------------------------- + +// Simplified structure to mimic a Document model +struct MyDocument +{ + const char* Name; // Document title + bool Open; // Set when the document is open (in this demo, we keep an array of all available documents to simplify the demo) + bool OpenPrev; // Copy of Open from last update. + bool Dirty; // Set when the document has been modified + bool WantClose; // Set when the document + ImVec4 Color; // An arbitrary variable associated to the document + + MyDocument(const char* name, bool open = true, const ImVec4& color = ImVec4(1.0f,1.0f,1.0f,1.0f)) + { + Name = name; + Open = OpenPrev = open; + Dirty = false; + WantClose = false; + Color = color; + } + void DoOpen() { Open = true; } + void DoQueueClose() { WantClose = true; } + void DoForceClose() { Open = false; Dirty = false; } + void DoSave() { Dirty = false; } + + // Display dummy contents for the Document + static void DisplayContents(MyDocument* doc) + { + ImGui::PushID(doc); + ImGui::Text("Document \"%s\"", doc->Name); + ImGui::PushStyleColor(ImGuiCol_Text, doc->Color); + ImGui::TextWrapped("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."); + ImGui::PopStyleColor(); + if (ImGui::Button("Modify", ImVec2(100, 0))) + doc->Dirty = true; + ImGui::SameLine(); + if (ImGui::Button("Save", ImVec2(100, 0))) + doc->DoSave(); + ImGui::ColorEdit3("color", &doc->Color.x); // Useful to test drag and drop and hold-dragged-to-open-tab behavior. + ImGui::PopID(); + } + + // Display context menu for the Document + static void DisplayContextMenu(MyDocument* doc) + { + if (!ImGui::BeginPopupContextItem()) + return; + + char buf[256]; + sprintf(buf, "Save %s", doc->Name); + if (ImGui::MenuItem(buf, "CTRL+S", false, doc->Open)) + doc->DoSave(); + if (ImGui::MenuItem("Close", "CTRL+W", false, doc->Open)) + doc->DoQueueClose(); + ImGui::EndPopup(); + } +}; + +struct ExampleAppDocuments +{ + ImVector Documents; + + ExampleAppDocuments() + { + Documents.push_back(MyDocument("Lettuce", true, ImVec4(0.4f, 0.8f, 0.4f, 1.0f))); + Documents.push_back(MyDocument("Eggplant", true, ImVec4(0.8f, 0.5f, 1.0f, 1.0f))); + Documents.push_back(MyDocument("Carrot", true, ImVec4(1.0f, 0.8f, 0.5f, 1.0f))); + Documents.push_back(MyDocument("Tomato", false, ImVec4(1.0f, 0.3f, 0.4f, 1.0f))); + Documents.push_back(MyDocument("A Rather Long Title", false)); + Documents.push_back(MyDocument("Some Document", false)); + } +}; + +// [Optional] Notify the system of Tabs/Windows closure that happened outside the regular tab interface. +// If a tab has been closed programmatically (aka closed from another source such as the Checkbox() in the demo, as opposed +// to clicking on the regular tab closing button) and stops being submitted, it will take a frame for the tab bar to notice its absence. +// During this frame there will be a gap in the tab bar, and if the tab that has disappeared was the selected one, the tab bar +// will report no selected tab during the frame. This will effectively give the impression of a flicker for one frame. +// We call SetTabItemClosed() to manually notify the Tab Bar or Docking system of removed tabs to avoid this glitch. +// Note that this completely optional, and only affect tab bars with the ImGuiTabBarFlags_Reorderable flag. +static void NotifyOfDocumentsClosedElsewhere(ExampleAppDocuments& app) +{ + for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) + { + MyDocument* doc = &app.Documents[doc_n]; + if (!doc->Open && doc->OpenPrev) + ImGui::SetTabItemClosed(doc->Name); + doc->OpenPrev = doc->Open; + } +} + +void ShowExampleAppDocuments(bool* p_open) +{ + static ExampleAppDocuments app; + + if (!ImGui::Begin("Example: Documents", p_open, ImGuiWindowFlags_MenuBar)) + { + ImGui::End(); + return; + } + + // Options + static bool opt_reorderable = true; + static ImGuiTabBarFlags opt_fitting_flags = ImGuiTabBarFlags_FittingPolicyDefault_; + + // Menu + if (ImGui::BeginMenuBar()) + { + if (ImGui::BeginMenu("File")) + { + int open_count = 0; + for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) + open_count += app.Documents[doc_n].Open ? 1 : 0; + + if (ImGui::BeginMenu("Open", open_count < app.Documents.Size)) + { + for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) + { + MyDocument* doc = &app.Documents[doc_n]; + if (!doc->Open) + if (ImGui::MenuItem(doc->Name)) + doc->DoOpen(); + } + ImGui::EndMenu(); } + if (ImGui::MenuItem("Close All Documents", NULL, false, open_count > 0)) + for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) + app.Documents[doc_n].DoQueueClose(); + if (ImGui::MenuItem("Exit", "Alt+F4")) {} + ImGui::EndMenu(); } - draw_list->PushClipRect(canvas_pos, ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y), true); // clip lines within the canvas (if we resize it, etc.) - for (int i = 0; i < points.Size - 1; i += 2) - draw_list->AddLine(ImVec2(canvas_pos.x + points[i].x, canvas_pos.y + points[i].y), ImVec2(canvas_pos.x + points[i + 1].x, canvas_pos.y + points[i + 1].y), IM_COL32(255, 255, 0, 255), 2.0f); - draw_list->PopClipRect(); - if (adding_preview) - points.pop_back(); + ImGui::EndMenuBar(); + } + + // [Debug] List documents with one checkbox for each + for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) + { + MyDocument* doc = &app.Documents[doc_n]; + if (doc_n > 0) + ImGui::SameLine(); + ImGui::PushID(doc); + if (ImGui::Checkbox(doc->Name, &doc->Open)) + if (!doc->Open) + doc->DoForceClose(); + ImGui::PopID(); } + + ImGui::Separator(); + + // Submit Tab Bar and Tabs + { + ImGuiTabBarFlags tab_bar_flags = (opt_fitting_flags) | (opt_reorderable ? ImGuiTabBarFlags_Reorderable : 0); + if (ImGui::BeginTabBar("##tabs", tab_bar_flags)) + { + if (opt_reorderable) + NotifyOfDocumentsClosedElsewhere(app); + + // [DEBUG] Stress tests + //if ((ImGui::GetFrameCount() % 30) == 0) docs[1].Open ^= 1; // [DEBUG] Automatically show/hide a tab. Test various interactions e.g. dragging with this on. + //if (ImGui::GetIO().KeyCtrl) ImGui::SetTabItemSelected(docs[1].Name); // [DEBUG] Test SetTabItemSelected(), probably not very useful as-is anyway.. + + // Submit Tabs + for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) + { + MyDocument* doc = &app.Documents[doc_n]; + if (!doc->Open) + continue; + + ImGuiTabItemFlags tab_flags = (doc->Dirty ? ImGuiTabItemFlags_UnsavedDocument : 0); + bool visible = ImGui::BeginTabItem(doc->Name, &doc->Open, tab_flags); + + // Cancel attempt to close when unsaved add to save queue so we can display a popup. + if (!doc->Open && doc->Dirty) + { + doc->Open = true; + doc->DoQueueClose(); + } + + MyDocument::DisplayContextMenu(doc); + if (visible) + { + MyDocument::DisplayContents(doc); + ImGui::EndTabItem(); + } + } + + ImGui::EndTabBar(); + } + } + + // Update closing queue + static ImVector close_queue; + if (close_queue.empty()) + { + // Close queue is locked once we started a popup + for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) + { + MyDocument* doc = &app.Documents[doc_n]; + if (doc->WantClose) + { + doc->WantClose = false; + close_queue.push_back(doc); + } + } + } + + // Display closing confirmation UI + if (!close_queue.empty()) + { + int close_queue_unsaved_documents = 0; + for (int n = 0; n < close_queue.Size; n++) + if (close_queue[n]->Dirty) + close_queue_unsaved_documents++; + + if (close_queue_unsaved_documents == 0) + { + // Close documents when all are unsaved + for (int n = 0; n < close_queue.Size; n++) + close_queue[n]->DoForceClose(); + close_queue.clear(); + } + else + { + if (!ImGui::IsPopupOpen("Save?")) + ImGui::OpenPopup("Save?"); + if (ImGui::BeginPopupModal("Save?")) + { + ImGui::Text("Save change to the following items?"); + ImGui::PushItemWidth(-1.0f); + ImGui::ListBoxHeader("##", close_queue_unsaved_documents, 6); + for (int n = 0; n < close_queue.Size; n++) + if (close_queue[n]->Dirty) + ImGui::Text("%s", close_queue[n]->Name); + ImGui::ListBoxFooter(); + + if (ImGui::Button("Yes", ImVec2(80, 0))) + { + for (int n = 0; n < close_queue.Size; n++) + { + if (close_queue[n]->Dirty) + close_queue[n]->DoSave(); + close_queue[n]->DoForceClose(); + } + close_queue.clear(); + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if (ImGui::Button("No", ImVec2(80, 0))) + { + for (int n = 0; n < close_queue.Size; n++) + close_queue[n]->DoForceClose(); + close_queue.clear(); + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if (ImGui::Button("Cancel", ImVec2(80, 0))) + { + close_queue.clear(); + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + } + } + ImGui::End(); } // End of Demo code #else +void ImGui::ShowAboutWindow(bool*) {} void ImGui::ShowDemoWindow(bool*) {} void ImGui::ShowUserGuide() {} void ImGui::ShowStyleEditor(ImGuiStyle*) {} diff --git a/src/lib/geogram_gfx/third_party/ImGui/imgui_draw.cpp b/src/lib/geogram_gfx/third_party/ImGui/imgui_draw.cpp index 7c8da648..a237d51e 100644 --- a/src/lib/geogram_gfx/third_party/ImGui/imgui_draw.cpp +++ b/src/lib/geogram_gfx/third_party/ImGui/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.66 +// dear imgui, v1.69 // (drawing and font code) /* @@ -12,7 +12,8 @@ Index of this file: // [SECTION] Helpers ShadeVertsXXX functions // [SECTION] ImFontConfig // [SECTION] ImFontAtlas -// [SECTION] ImFontAtlas glyph ranges helpers + GlyphRangesBuilder +// [SECTION] ImFontAtlas glyph ranges helpers +// [SECTION] ImFontGlyphRangesBuilder // [SECTION] ImFont // [SECTION] Internal Render Helpers // [SECTION] Decompression code @@ -32,7 +33,7 @@ Index of this file: #include // vsnprintf, sscanf, printf #if !defined(alloca) -#if defined(__GLIBC__) || defined(__sun) || defined(__CYGWIN__) +#if defined(__GLIBC__) || defined(__sun) || defined(__CYGWIN__) || defined(__APPLE__) #include // alloca (glibc uses . Note that Cygwin may have _WIN32 defined, so the order matters here) #elif defined(_WIN32) #include // alloca @@ -55,8 +56,11 @@ Index of this file: #pragma clang diagnostic ignored "-Wunknown-pragmas" // [Bruno Levy] 05/23/2016: so that it does not complain on older compilers #pragma clang diagnostic ignored "-Wold-style-cast" // warning : use of old-style cast // yes, they are more terse. #pragma clang diagnostic ignored "-Wfloat-equal" // warning : comparing floating point with == or != is unsafe // storing and comparing against same constants ok. -#pragma clang diagnostic ignored "-Wglobal-constructors" // warning : declaration requires a global destructor // similar to above, not sure what the exact difference it. +#pragma clang diagnostic ignored "-Wglobal-constructors" // warning : declaration requires a global destructor // similar to above, not sure what the exact difference is. #pragma clang diagnostic ignored "-Wsign-conversion" // warning : implicit conversion changes signedness // +#if __has_warning("-Wzero-as-null-pointer-constant") +#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning : zero as null pointer constant // some standard header variations use #define NULL 0 +#endif #if __has_warning("-Wcomma") #pragma clang diagnostic ignored "-Wcomma" // warning : possible misuse of comma operator here // #endif @@ -64,7 +68,7 @@ Index of this file: #pragma clang diagnostic ignored "-Wreserved-id-macro" // warning : macro name is a reserved identifier // #endif #if __has_warning("-Wdouble-promotion") -#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function +#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double. #endif #elif defined(__GNUC__) #pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used @@ -80,7 +84,7 @@ Index of this file: //------------------------------------------------------------------------- // Compile time options: -//#define IMGUI_STB_NAMESPACE ImGuiStb +//#define IMGUI_STB_NAMESPACE ImStb //#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h" //#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h" //#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION @@ -160,7 +164,7 @@ namespace IMGUI_STB_NAMESPACE #endif #ifdef IMGUI_STB_NAMESPACE -} // namespace ImGuiStb +} // namespace ImStb using namespace IMGUI_STB_NAMESPACE; #endif @@ -176,7 +180,7 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst) colors[ImGuiCol_Text] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImGuiCol_TextDisabled] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f); colors[ImGuiCol_WindowBg] = ImVec4(0.06f, 0.06f, 0.06f, 0.94f); - colors[ImGuiCol_ChildBg] = ImVec4(1.00f, 1.00f, 1.00f, 0.00f); + colors[ImGuiCol_ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); colors[ImGuiCol_PopupBg] = ImVec4(0.08f, 0.08f, 0.08f, 0.94f); colors[ImGuiCol_Border] = ImVec4(0.43f, 0.43f, 0.50f, 0.50f); colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); @@ -206,6 +210,11 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst) colors[ImGuiCol_ResizeGrip] = ImVec4(0.26f, 0.59f, 0.98f, 0.25f); colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f); colors[ImGuiCol_ResizeGripActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f); + colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.80f); + colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered]; + colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f); + colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f); + colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f); colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f); colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f); colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); @@ -256,6 +265,11 @@ void ImGui::StyleColorsClassic(ImGuiStyle* dst) colors[ImGuiCol_ResizeGrip] = ImVec4(1.00f, 1.00f, 1.00f, 0.16f); colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.78f, 0.82f, 1.00f, 0.60f); colors[ImGuiCol_ResizeGripActive] = ImVec4(0.78f, 0.82f, 1.00f, 0.90f); + colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.80f); + colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered]; + colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f); + colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f); + colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f); colors[ImGuiCol_PlotLines] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImGuiCol_PlotLinesHovered] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); @@ -307,6 +321,11 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst) colors[ImGuiCol_ResizeGrip] = ImVec4(0.80f, 0.80f, 0.80f, 0.56f); colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f); colors[ImGuiCol_ResizeGripActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f); + colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.90f); + colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered]; + colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f); + colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f); + colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f); colors[ImGuiCol_PlotLines] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f); colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f); colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); @@ -389,7 +408,7 @@ ImDrawList* ImDrawList::CloneOutput() const // Using macros because C++ is a terrible language, we want guaranteed inline, no code in header, and no overhead in Debug builds #define GetCurrentClipRect() (_ClipRectStack.Size ? _ClipRectStack.Data[_ClipRectStack.Size-1] : _Data->ClipRectFullscreen) -#define GetCurrentTextureId() (_TextureIdStack.Size ? _TextureIdStack.Data[_TextureIdStack.Size-1] : NULL) +#define GetCurrentTextureId() (_TextureIdStack.Size ? _TextureIdStack.Data[_TextureIdStack.Size-1] : (ImTextureID)NULL) void ImDrawList::AddDrawCmd() { @@ -641,7 +660,13 @@ void ImDrawList::PrimQuadUV(const ImVec2& a, const ImVec2& b, const ImVec2& c, c _IdxWritePtr += 6; } +// On AddPolyline() and AddConvexPolyFilled() we intentionally avoid using ImVec2 and superflous function calls to optimize debug/non-inlined builds. +// Those macros expects l-values. +#define IM_NORMALIZE2F_OVER_ZERO(VX,VY) { float d2 = VX*VX + VY*VY; if (d2 > 0.0f) { float inv_len = 1.0f / ImSqrt(d2); VX *= inv_len; VY *= inv_len; } } +#define IM_NORMALIZE2F_OVER_EPSILON_CLAMP(VX,VY,EPS,INVLENMAX) { float d2 = VX*VX + VY*VY; if (d2 > EPS) { float inv_len = 1.0f / ImSqrt(d2); if (inv_len > INVLENMAX) inv_len = INVLENMAX; VX *= inv_len; VY *= inv_len; } } + // TODO: Thickness anti-aliased lines cap are missing their AA fringe. +// We avoid using the ImVec2 math operators here to reduce cost to a minimum for debug/non-inlined builds. void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 col, bool closed, float thickness) { if (points_count < 2) @@ -665,16 +690,17 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 PrimReserve(idx_count, vtx_count); // Temporary buffer - ImVec2* temp_normals = (ImVec2*)alloca(points_count * (thick_line ? 5 : 3) * sizeof(ImVec2)); + ImVec2* temp_normals = (ImVec2*)alloca(points_count * (thick_line ? 5 : 3) * sizeof(ImVec2)); //-V630 ImVec2* temp_points = temp_normals + points_count; for (int i1 = 0; i1 < count; i1++) { const int i2 = (i1+1) == points_count ? 0 : i1+1; - ImVec2 diff = points[i2] - points[i1]; - diff *= ImInvLength(diff, 1.0f); - temp_normals[i1].x = diff.y; - temp_normals[i1].y = -diff.x; + float dx = points[i2].x - points[i1].x; + float dy = points[i2].y - points[i1].y; + IM_NORMALIZE2F_OVER_ZERO(dx, dy); + temp_normals[i1].x = dy; + temp_normals[i1].y = -dx; } if (!closed) temp_normals[points_count-1] = temp_normals[points_count-2]; @@ -697,17 +723,18 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 unsigned int idx2 = (i1+1) == points_count ? _VtxCurrentIdx : idx1+3; // Average normals - ImVec2 dm = (temp_normals[i1] + temp_normals[i2]) * 0.5f; - float dmr2 = dm.x*dm.x + dm.y*dm.y; - if (dmr2 > 0.000001f) - { - float scale = 1.0f / dmr2; - if (scale > 100.0f) scale = 100.0f; - dm *= scale; - } - dm *= AA_SIZE; - temp_points[i2*2+0] = points[i2] + dm; - temp_points[i2*2+1] = points[i2] - dm; + float dm_x = (temp_normals[i1].x + temp_normals[i2].x) * 0.5f; + float dm_y = (temp_normals[i1].y + temp_normals[i2].y) * 0.5f; + IM_NORMALIZE2F_OVER_EPSILON_CLAMP(dm_x, dm_y, 0.000001f, 100.0f) + dm_x *= AA_SIZE; + dm_y *= AA_SIZE; + + // Add temporary vertexes + ImVec2* out_vtx = &temp_points[i2*2]; + out_vtx[0].x = points[i2].x + dm_x; + out_vtx[0].y = points[i2].y + dm_y; + out_vtx[1].x = points[i2].x - dm_x; + out_vtx[1].y = points[i2].y - dm_y; // Add indexes _IdxWritePtr[0] = (ImDrawIdx)(idx2+0); _IdxWritePtr[1] = (ImDrawIdx)(idx1+0); _IdxWritePtr[2] = (ImDrawIdx)(idx1+2); @@ -751,20 +778,24 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 unsigned int idx2 = (i1+1) == points_count ? _VtxCurrentIdx : idx1+4; // Average normals - ImVec2 dm = (temp_normals[i1] + temp_normals[i2]) * 0.5f; - float dmr2 = dm.x*dm.x + dm.y*dm.y; - if (dmr2 > 0.000001f) - { - float scale = 1.0f / dmr2; - if (scale > 100.0f) scale = 100.0f; - dm *= scale; - } - ImVec2 dm_out = dm * (half_inner_thickness + AA_SIZE); - ImVec2 dm_in = dm * half_inner_thickness; - temp_points[i2*4+0] = points[i2] + dm_out; - temp_points[i2*4+1] = points[i2] + dm_in; - temp_points[i2*4+2] = points[i2] - dm_in; - temp_points[i2*4+3] = points[i2] - dm_out; + float dm_x = (temp_normals[i1].x + temp_normals[i2].x) * 0.5f; + float dm_y = (temp_normals[i1].y + temp_normals[i2].y) * 0.5f; + IM_NORMALIZE2F_OVER_EPSILON_CLAMP(dm_x, dm_y, 0.000001f, 100.0f); + float dm_out_x = dm_x * (half_inner_thickness + AA_SIZE); + float dm_out_y = dm_y * (half_inner_thickness + AA_SIZE); + float dm_in_x = dm_x * half_inner_thickness; + float dm_in_y = dm_y * half_inner_thickness; + + // Add temporary vertexes + ImVec2* out_vtx = &temp_points[i2*4]; + out_vtx[0].x = points[i2].x + dm_out_x; + out_vtx[0].y = points[i2].y + dm_out_y; + out_vtx[1].x = points[i2].x + dm_in_x; + out_vtx[1].y = points[i2].y + dm_in_y; + out_vtx[2].x = points[i2].x - dm_in_x; + out_vtx[2].y = points[i2].y - dm_in_y; + out_vtx[3].x = points[i2].x - dm_out_x; + out_vtx[3].y = points[i2].y - dm_out_y; // Add indexes _IdxWritePtr[0] = (ImDrawIdx)(idx2+1); _IdxWritePtr[1] = (ImDrawIdx)(idx1+1); _IdxWritePtr[2] = (ImDrawIdx)(idx1+2); @@ -802,11 +833,13 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 const int i2 = (i1+1) == points_count ? 0 : i1+1; const ImVec2& p1 = points[i1]; const ImVec2& p2 = points[i2]; - ImVec2 diff = p2 - p1; - diff *= ImInvLength(diff, 1.0f); - const float dx = diff.x * (thickness * 0.5f); - const float dy = diff.y * (thickness * 0.5f); + float dx = p2.x - p1.x; + float dy = p2.y - p1.y; + IM_NORMALIZE2F_OVER_ZERO(dx, dy); + dx *= (thickness * 0.5f); + dy *= (thickness * 0.5f); + _VtxWritePtr[0].pos.x = p1.x + dy; _VtxWritePtr[0].pos.y = p1.y - dx; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; _VtxWritePtr[1].pos.x = p2.x + dy; _VtxWritePtr[1].pos.y = p2.y - dx; _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col; _VtxWritePtr[2].pos.x = p2.x - dy; _VtxWritePtr[2].pos.y = p2.y + dx; _VtxWritePtr[2].uv = uv; _VtxWritePtr[2].col = col; @@ -821,6 +854,7 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 } } +// We intentionally avoid using ImVec2 and its math operators here to reduce cost to a minimum for debug/non-inlined builds. void ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_count, ImU32 col) { if (points_count < 3) @@ -847,15 +881,16 @@ void ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_coun } // Compute normals - ImVec2* temp_normals = (ImVec2*)alloca(points_count * sizeof(ImVec2)); + ImVec2* temp_normals = (ImVec2*)alloca(points_count * sizeof(ImVec2)); //-V630 for (int i0 = points_count-1, i1 = 0; i1 < points_count; i0 = i1++) { const ImVec2& p0 = points[i0]; const ImVec2& p1 = points[i1]; - ImVec2 diff = p1 - p0; - diff *= ImInvLength(diff, 1.0f); - temp_normals[i0].x = diff.y; - temp_normals[i0].y = -diff.x; + float dx = p1.x - p0.x; + float dy = p1.y - p0.y; + IM_NORMALIZE2F_OVER_ZERO(dx, dy); + temp_normals[i0].x = dy; + temp_normals[i0].y = -dx; } for (int i0 = points_count-1, i1 = 0; i1 < points_count; i0 = i1++) @@ -863,19 +898,15 @@ void ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_coun // Average normals const ImVec2& n0 = temp_normals[i0]; const ImVec2& n1 = temp_normals[i1]; - ImVec2 dm = (n0 + n1) * 0.5f; - float dmr2 = dm.x*dm.x + dm.y*dm.y; - if (dmr2 > 0.000001f) - { - float scale = 1.0f / dmr2; - if (scale > 100.0f) scale = 100.0f; - dm *= scale; - } - dm *= AA_SIZE * 0.5f; + float dm_x = (n0.x + n1.x) * 0.5f; + float dm_y = (n0.y + n1.y) * 0.5f; + IM_NORMALIZE2F_OVER_EPSILON_CLAMP(dm_x, dm_y, 0.000001f, 100.0f); + dm_x *= AA_SIZE * 0.5f; + dm_y *= AA_SIZE * 0.5f; // Add vertices - _VtxWritePtr[0].pos = (points[i1] - dm); _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; // Inner - _VtxWritePtr[1].pos = (points[i1] + dm); _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col_trans; // Outer + _VtxWritePtr[0].pos.x = (points[i1].x - dm_x); _VtxWritePtr[0].pos.y = (points[i1].y - dm_y); _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; // Inner + _VtxWritePtr[1].pos.x = (points[i1].x + dm_x); _VtxWritePtr[1].pos.y = (points[i1].y + dm_y); _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col_trans; // Outer _VtxWritePtr += 2; // Add indexes for fringes @@ -927,6 +958,9 @@ void ImDrawList::PathArcTo(const ImVec2& centre, float radius, float a_min, floa _Path.push_back(centre); return; } + + // Note that we are adding a point at both a_min and a_max. + // If you are trying to draw a full closed circle you don't want the overlapping points! _Path.reserve(_Path.Size + (num_segments + 1)); for (int i = 0; i <= num_segments; i++) { @@ -1110,21 +1144,23 @@ void ImDrawList::AddTriangleFilled(const ImVec2& a, const ImVec2& b, const ImVec void ImDrawList::AddCircle(const ImVec2& centre, float radius, ImU32 col, int num_segments, float thickness) { - if ((col & IM_COL32_A_MASK) == 0) + if ((col & IM_COL32_A_MASK) == 0 || num_segments <= 2) return; + // Because we are filling a closed shape we remove 1 from the count of segments/points const float a_max = IM_PI*2.0f * ((float)num_segments - 1.0f) / (float)num_segments; - PathArcTo(centre, radius-0.5f, 0.0f, a_max, num_segments); + PathArcTo(centre, radius-0.5f, 0.0f, a_max, num_segments - 1); PathStroke(col, true, thickness); } void ImDrawList::AddCircleFilled(const ImVec2& centre, float radius, ImU32 col, int num_segments) { - if ((col & IM_COL32_A_MASK) == 0) + if ((col & IM_COL32_A_MASK) == 0 || num_segments <= 2) return; + // Because we are filling a closed shape we remove 1 from the count of segments/points const float a_max = IM_PI*2.0f * ((float)num_segments - 1.0f) / (float)num_segments; - PathArcTo(centre, radius, 0.0f, a_max, num_segments); + PathArcTo(centre, radius, 0.0f, a_max, num_segments - 1); PathFillConvex(col); } @@ -1252,8 +1288,10 @@ void ImDrawData::DeIndexAllBuffers() } } -// Helper to scale the ClipRect field of each ImDrawCmd. Use if your final output buffer is at a different scale than ImGui expects, or if there is a difference between your window resolution and framebuffer resolution. -void ImDrawData::ScaleClipRects(const ImVec2& scale) +// Helper to scale the ClipRect field of each ImDrawCmd. +// Use if your final output buffer is at a different scale than draw_data->DisplaySize, +// or if there is a difference between your window resolution and framebuffer resolution. +void ImDrawData::ScaleClipRects(const ImVec2& fb_scale) { for (int i = 0; i < CmdListsCount; i++) { @@ -1261,7 +1299,7 @@ void ImDrawData::ScaleClipRects(const ImVec2& scale) for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) { ImDrawCmd* cmd = &cmd_list->CmdBuffer[cmd_i]; - cmd->ClipRect = ImVec4(cmd->ClipRect.x * scale.x, cmd->ClipRect.y * scale.y, cmd->ClipRect.z * scale.x, cmd->ClipRect.w * scale.y); + cmd->ClipRect = ImVec4(cmd->ClipRect.x * fb_scale.x, cmd->ClipRect.y * fb_scale.y, cmd->ClipRect.z * fb_scale.x, cmd->ClipRect.w * fb_scale.y); } } } @@ -1324,7 +1362,7 @@ ImFontConfig::ImFontConfig() FontDataOwnedByAtlas = true; FontNo = 0; SizePixels = 0.0f; - OversampleH = 3; + OversampleH = 3; // FIXME: 2 may be a better default? OversampleV = 1; PixelSnapH = false; GlyphExtraSpacing = ImVec2(0.0f, 0.0f); @@ -1490,7 +1528,7 @@ void ImFontAtlas::GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_wid GetTexDataAsAlpha8(&pixels, NULL, NULL); if (pixels) { - TexPixelsRGBA32 = (unsigned int*)ImGui::MemAlloc((size_t)(TexWidth * TexHeight * 4)); + TexPixelsRGBA32 = (unsigned int*)ImGui::MemAlloc((size_t)TexWidth * (size_t)TexHeight * 4); const unsigned char* src = pixels; unsigned int* dst = TexPixelsRGBA32; for (int n = TexWidth * TexHeight; n > 0; n--) @@ -1514,11 +1552,11 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg) if (!font_cfg->MergeMode) Fonts.push_back(IM_NEW(ImFont)); else - IM_ASSERT(!Fonts.empty()); // When using MergeMode make sure that a font has already been added before. You can use ImGui::GetIO().Fonts->AddFontDefault() to add the default imgui font. + IM_ASSERT(!Fonts.empty() && "Cannot use MergeMode for the first font"); // When using MergeMode make sure that a font has already been added before. You can use ImGui::GetIO().Fonts->AddFontDefault() to add the default imgui font. ConfigData.push_back(*font_cfg); ImFontConfig& new_font_cfg = ConfigData.back(); - if (!new_font_cfg.DstFont) + if (new_font_cfg.DstFont == NULL) new_font_cfg.DstFont = Fonts.back(); if (!new_font_cfg.FontDataOwnedByAtlas) { @@ -1557,8 +1595,10 @@ ImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg_template) font_cfg.OversampleH = font_cfg.OversampleV = 1; font_cfg.PixelSnapH = true; } - if (font_cfg.Name[0] == '\0') strcpy(font_cfg.Name, "ProggyClean.ttf, 13px"); - if (font_cfg.SizePixels <= 0.0f) font_cfg.SizePixels = 13.0f; + if (font_cfg.SizePixels <= 0.0f) + font_cfg.SizePixels = 13.0f * 1.0f; + if (font_cfg.Name[0] == '\0') + ImFormatString(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "ProggyClean.ttf, %dpx", (int)font_cfg.SizePixels); const char* ttf_compressed_base85 = GetDefaultCompressedFontDataTTFBase85(); const ImWchar* glyph_ranges = font_cfg.GlyphRanges != NULL ? font_cfg.GlyphRanges : GetGlyphRangesDefault(); @@ -1706,143 +1746,222 @@ void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsig data[i] = table[data[i]]; } +// Temporary data for one source font (multiple source fonts can be merged into one destination ImFont) +// (C++03 doesn't allow instancing ImVector<> with function-local types so we declare the type here.) +struct ImFontBuildSrcData +{ + stbtt_fontinfo FontInfo; + stbtt_pack_range PackRange; // Hold the list of codepoints to pack (essentially points to Codepoints.Data) + stbrp_rect* Rects; // Rectangle to pack. We first fill in their size and the packer will give us their position. + stbtt_packedchar* PackedChars; // Output glyphs + const ImWchar* SrcRanges; // Ranges as requested by user (user is allowed to request too much, e.g. 0x0020..0xFFFF) + int DstIndex; // Index into atlas->Fonts[] and dst_tmp_array[] + int GlyphsHighest; // Highest requested codepoint + int GlyphsCount; // Glyph count (excluding missing glyphs and glyphs already set by an earlier source font) + ImBoolVector GlyphsSet; // Glyph bit map (random access, 1-bit per codepoint. This will be a maximum of 8KB) + ImVector GlyphsList; // Glyph codepoints list (flattened version of GlyphsMap) +}; + +// Temporary data for one destination ImFont* (multiple source fonts can be merged into one destination ImFont) +struct ImFontBuildDstData +{ + int SrcCount; // Number of source fonts targeting this destination font. + int GlyphsHighest; + int GlyphsCount; + ImBoolVector GlyphsSet; // This is used to resolve collision when multiple sources are merged into a same destination font. +}; + +static void UnpackBoolVectorToFlatIndexList(const ImBoolVector* in, ImVector* out) +{ + IM_ASSERT(sizeof(in->Storage.Data[0]) == sizeof(int)); + const int* it_begin = in->Storage.begin(); + const int* it_end = in->Storage.end(); + for (const int* it = it_begin; it < it_end; it++) + if (int entries_32 = *it) + for (int bit_n = 0; bit_n < 32; bit_n++) + if (entries_32 & (1 << bit_n)) + out->push_back((int)((it - it_begin) << 5) + bit_n); +} + bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) { IM_ASSERT(atlas->ConfigData.Size > 0); ImFontAtlasBuildRegisterDefaultCustomRects(atlas); + // Clear atlas atlas->TexID = (ImTextureID)NULL; atlas->TexWidth = atlas->TexHeight = 0; atlas->TexUvScale = ImVec2(0.0f, 0.0f); atlas->TexUvWhitePixel = ImVec2(0.0f, 0.0f); atlas->ClearTexData(); - // Count glyphs/ranges + // Temporary storage for building + ImVector src_tmp_array; + ImVector dst_tmp_array; + src_tmp_array.resize(atlas->ConfigData.Size); + dst_tmp_array.resize(atlas->Fonts.Size); + memset(src_tmp_array.Data, 0, (size_t)src_tmp_array.size_in_bytes()); + memset(dst_tmp_array.Data, 0, (size_t)dst_tmp_array.size_in_bytes()); + + // 1. Initialize font loading structure, check font data validity + for (int src_i = 0; src_i < atlas->ConfigData.Size; src_i++) + { + ImFontBuildSrcData& src_tmp = src_tmp_array[src_i]; + ImFontConfig& cfg = atlas->ConfigData[src_i]; + IM_ASSERT(cfg.DstFont && (!cfg.DstFont->IsLoaded() || cfg.DstFont->ContainerAtlas == atlas)); + + // Find index from cfg.DstFont (we allow the user to set cfg.DstFont. Also it makes casual debugging nicer than when storing indices) + src_tmp.DstIndex = -1; + for (int output_i = 0; output_i < atlas->Fonts.Size && src_tmp.DstIndex == -1; output_i++) + if (cfg.DstFont == atlas->Fonts[output_i]) + src_tmp.DstIndex = output_i; + IM_ASSERT(src_tmp.DstIndex != -1); // cfg.DstFont not pointing within atlas->Fonts[] array? + if (src_tmp.DstIndex == -1) + return false; + + // Initialize helper structure for font loading and verify that the TTF/OTF data is correct + const int font_offset = stbtt_GetFontOffsetForIndex((unsigned char*)cfg.FontData, cfg.FontNo); + IM_ASSERT(font_offset >= 0 && "FontData is incorrect, or FontNo cannot be found."); + if (!stbtt_InitFont(&src_tmp.FontInfo, (unsigned char*)cfg.FontData, font_offset)) + return false; + + // Measure highest codepoints + ImFontBuildDstData& dst_tmp = dst_tmp_array[src_tmp.DstIndex]; + src_tmp.SrcRanges = cfg.GlyphRanges ? cfg.GlyphRanges : atlas->GetGlyphRangesDefault(); + for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2) + src_tmp.GlyphsHighest = ImMax(src_tmp.GlyphsHighest, (int)src_range[1]); + dst_tmp.SrcCount++; + dst_tmp.GlyphsHighest = ImMax(dst_tmp.GlyphsHighest, src_tmp.GlyphsHighest); + } + + // 2. For every requested codepoint, check for their presence in the font data, and handle redundancy or overlaps between source fonts to avoid unused glyphs. int total_glyphs_count = 0; - int total_ranges_count = 0; - for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++) + for (int src_i = 0; src_i < src_tmp_array.Size; src_i++) { - ImFontConfig& cfg = atlas->ConfigData[input_i]; - if (!cfg.GlyphRanges) - cfg.GlyphRanges = atlas->GetGlyphRangesDefault(); - for (const ImWchar* in_range = cfg.GlyphRanges; in_range[0] && in_range[1]; in_range += 2, total_ranges_count++) - total_glyphs_count += (in_range[1] - in_range[0]) + 1; + ImFontBuildSrcData& src_tmp = src_tmp_array[src_i]; + ImFontBuildDstData& dst_tmp = dst_tmp_array[src_tmp.DstIndex]; + src_tmp.GlyphsSet.Resize(src_tmp.GlyphsHighest + 1); + if (dst_tmp.GlyphsSet.Storage.empty()) + dst_tmp.GlyphsSet.Resize(dst_tmp.GlyphsHighest + 1); + + for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2) + for (int codepoint = src_range[0]; codepoint <= src_range[1]; codepoint++) + { + if (dst_tmp.GlyphsSet.GetBit(codepoint)) // Don't overwrite existing glyphs. We could make this an option for MergeMode (e.g. MergeOverwrite==true) + continue; + if (!stbtt_FindGlyphIndex(&src_tmp.FontInfo, codepoint)) // It is actually in the font? + continue; + + // Add to avail set/counters + src_tmp.GlyphsCount++; + dst_tmp.GlyphsCount++; + src_tmp.GlyphsSet.SetBit(codepoint, true); + dst_tmp.GlyphsSet.SetBit(codepoint, true); + total_glyphs_count++; + } + } + + // 3. Unpack our bit map into a flat list (we now have all the Unicode points that we know are requested _and_ available _and_ not overlapping another) + for (int src_i = 0; src_i < src_tmp_array.Size; src_i++) + { + ImFontBuildSrcData& src_tmp = src_tmp_array[src_i]; + src_tmp.GlyphsList.reserve(src_tmp.GlyphsCount); + UnpackBoolVectorToFlatIndexList(&src_tmp.GlyphsSet, &src_tmp.GlyphsList); + src_tmp.GlyphsSet.Clear(); + IM_ASSERT(src_tmp.GlyphsList.Size == src_tmp.GlyphsCount); } + for (int dst_i = 0; dst_i < dst_tmp_array.Size; dst_i++) + dst_tmp_array[dst_i].GlyphsSet.Clear(); + dst_tmp_array.clear(); + + // Allocate packing character data and flag packed characters buffer as non-packed (x0=y0=x1=y1=0) + // (We technically don't need to zero-clear buf_rects, but let's do it for the sake of sanity) + ImVector buf_rects; + ImVector buf_packedchars; + buf_rects.resize(total_glyphs_count); + buf_packedchars.resize(total_glyphs_count); + memset(buf_rects.Data, 0, (size_t)buf_rects.size_in_bytes()); + memset(buf_packedchars.Data, 0, (size_t)buf_packedchars.size_in_bytes()); + + // 4. Gather glyphs sizes so we can pack them in our virtual canvas. + int total_surface = 0; + int buf_rects_out_n = 0; + int buf_packedchars_out_n = 0; + for (int src_i = 0; src_i < src_tmp_array.Size; src_i++) + { + ImFontBuildSrcData& src_tmp = src_tmp_array[src_i]; + if (src_tmp.GlyphsCount == 0) + continue; - // We need a width for the skyline algorithm. Using a dumb heuristic here to decide of width. User can override TexDesiredWidth and TexGlyphPadding if they wish. - // Width doesn't really matter much, but some API/GPU have texture size limitations and increasing width can decrease height. - atlas->TexWidth = (atlas->TexDesiredWidth > 0) ? atlas->TexDesiredWidth : (total_glyphs_count > 4000) ? 4096 : (total_glyphs_count > 2000) ? 2048 : (total_glyphs_count > 1000) ? 1024 : 512; + src_tmp.Rects = &buf_rects[buf_rects_out_n]; + src_tmp.PackedChars = &buf_packedchars[buf_packedchars_out_n]; + buf_rects_out_n += src_tmp.GlyphsCount; + buf_packedchars_out_n += src_tmp.GlyphsCount; + + // Convert our ranges in the format stb_truetype wants + ImFontConfig& cfg = atlas->ConfigData[src_i]; + src_tmp.PackRange.font_size = cfg.SizePixels; + src_tmp.PackRange.first_unicode_codepoint_in_range = 0; + src_tmp.PackRange.array_of_unicode_codepoints = src_tmp.GlyphsList.Data; + src_tmp.PackRange.num_chars = src_tmp.GlyphsList.Size; + src_tmp.PackRange.chardata_for_range = src_tmp.PackedChars; + src_tmp.PackRange.h_oversample = (unsigned char)cfg.OversampleH; + src_tmp.PackRange.v_oversample = (unsigned char)cfg.OversampleV; + + // Gather the sizes of all rectangles we will need to pack (this loop is based on stbtt_PackFontRangesGatherRects) + const float scale = (cfg.SizePixels > 0) ? stbtt_ScaleForPixelHeight(&src_tmp.FontInfo, cfg.SizePixels) : stbtt_ScaleForMappingEmToPixels(&src_tmp.FontInfo, -cfg.SizePixels); + const int padding = atlas->TexGlyphPadding; + for (int glyph_i = 0; glyph_i < src_tmp.GlyphsList.Size; glyph_i++) + { + int x0, y0, x1, y1; + const int glyph_index_in_font = stbtt_FindGlyphIndex(&src_tmp.FontInfo, src_tmp.GlyphsList[glyph_i]); + IM_ASSERT(glyph_index_in_font != 0); + stbtt_GetGlyphBitmapBoxSubpixel(&src_tmp.FontInfo, glyph_index_in_font, scale * cfg.OversampleH, scale * cfg.OversampleV, 0, 0, &x0, &y0, &x1, &y1); + src_tmp.Rects[glyph_i].w = (stbrp_coord)(x1 - x0 + padding + cfg.OversampleH - 1); + src_tmp.Rects[glyph_i].h = (stbrp_coord)(y1 - y0 + padding + cfg.OversampleV - 1); + total_surface += src_tmp.Rects[glyph_i].w * src_tmp.Rects[glyph_i].h; + } + } + + // We need a width for the skyline algorithm, any width! + // The exact width doesn't really matter much, but some API/GPU have texture size limitations and increasing width can decrease height. + // User can override TexDesiredWidth and TexGlyphPadding if they wish, otherwise we use a simple heuristic to select the width based on expected surface. + const int surface_sqrt = (int)ImSqrt((float)total_surface) + 1; atlas->TexHeight = 0; + if (atlas->TexDesiredWidth > 0) + atlas->TexWidth = atlas->TexDesiredWidth; + else + atlas->TexWidth = (surface_sqrt >= 4096*0.7f) ? 4096 : (surface_sqrt >= 2048*0.7f) ? 2048 : (surface_sqrt >= 1024*0.7f) ? 1024 : 512; - // Start packing - const int max_tex_height = 1024*32; + // 5. Start packing + // Pack our extra data rectangles first, so it will be on the upper-left corner of our texture (UV will have small values). + const int TEX_HEIGHT_MAX = 1024 * 32; // [Bruno Levy]: replaced "={}" with memset() (={} generates a warning on MSVC). stbtt_pack_context spc; memset(&spc, 0, sizeof(spc)); - if (!stbtt_PackBegin(&spc, NULL, atlas->TexWidth, max_tex_height, 0, atlas->TexGlyphPadding, NULL)) - return false; - stbtt_PackSetOversampling(&spc, 1, 1); - - // Pack our extra data rectangles first, so it will be on the upper-left corner of our texture (UV will have small values). + stbtt_PackBegin(&spc, NULL, atlas->TexWidth, TEX_HEIGHT_MAX, 0, atlas->TexGlyphPadding, NULL); ImFontAtlasBuildPackCustomRects(atlas, spc.pack_info); - // Initialize font information (so we can error without any cleanup) - struct ImFontTempBuildData - { - stbtt_fontinfo FontInfo; - stbrp_rect* Rects; - int RectsCount; - stbtt_pack_range* Ranges; - int RangesCount; - }; - ImFontTempBuildData* tmp_array = (ImFontTempBuildData*)ImGui::MemAlloc((size_t)atlas->ConfigData.Size * sizeof(ImFontTempBuildData)); - for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++) + // 6. Pack each source font. No rendering yet, we are working with rectangles in an infinitely tall texture at this point. + for (int src_i = 0; src_i < src_tmp_array.Size; src_i++) { - ImFontConfig& cfg = atlas->ConfigData[input_i]; - ImFontTempBuildData& tmp = tmp_array[input_i]; - IM_ASSERT(cfg.DstFont && (!cfg.DstFont->IsLoaded() || cfg.DstFont->ContainerAtlas == atlas)); - - const int font_offset = stbtt_GetFontOffsetForIndex((unsigned char*)cfg.FontData, cfg.FontNo); - IM_ASSERT(font_offset >= 0 && "FontData is incorrect, or FontNo cannot be found."); - if (!stbtt_InitFont(&tmp.FontInfo, (unsigned char*)cfg.FontData, font_offset)) - { - atlas->TexWidth = atlas->TexHeight = 0; // Reset output on failure - ImGui::MemFree(tmp_array); - return false; - } - } + ImFontBuildSrcData& src_tmp = src_tmp_array[src_i]; + if (src_tmp.GlyphsCount == 0) + continue; - // Allocate packing character data and flag packed characters buffer as non-packed (x0=y0=x1=y1=0) - int buf_packedchars_n = 0, buf_rects_n = 0, buf_ranges_n = 0; - stbtt_packedchar* buf_packedchars = (stbtt_packedchar*)ImGui::MemAlloc(total_glyphs_count * sizeof(stbtt_packedchar)); - stbrp_rect* buf_rects = (stbrp_rect*)ImGui::MemAlloc(total_glyphs_count * sizeof(stbrp_rect)); - stbtt_pack_range* buf_ranges = (stbtt_pack_range*)ImGui::MemAlloc(total_ranges_count * sizeof(stbtt_pack_range)); - memset(buf_packedchars, 0, total_glyphs_count * sizeof(stbtt_packedchar)); - memset(buf_rects, 0, total_glyphs_count * sizeof(stbrp_rect)); // Unnecessary but let's clear this for the sake of sanity. - memset(buf_ranges, 0, total_ranges_count * sizeof(stbtt_pack_range)); - - // First font pass: pack all glyphs (no rendering at this point, we are working with rectangles in an infinitely tall texture at this point) - for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++) - { - ImFontConfig& cfg = atlas->ConfigData[input_i]; - ImFontTempBuildData& tmp = tmp_array[input_i]; - - // Setup ranges - int font_glyphs_count = 0; - int font_ranges_count = 0; - for (const ImWchar* in_range = cfg.GlyphRanges; in_range[0] && in_range[1]; in_range += 2, font_ranges_count++) - font_glyphs_count += (in_range[1] - in_range[0]) + 1; - tmp.Ranges = buf_ranges + buf_ranges_n; - tmp.RangesCount = font_ranges_count; - buf_ranges_n += font_ranges_count; - for (int i = 0; i < font_ranges_count; i++) - { - const ImWchar* in_range = &cfg.GlyphRanges[i * 2]; - stbtt_pack_range& range = tmp.Ranges[i]; - range.font_size = cfg.SizePixels; - range.first_unicode_codepoint_in_range = in_range[0]; - range.num_chars = (in_range[1] - in_range[0]) + 1; - range.chardata_for_range = buf_packedchars + buf_packedchars_n; - buf_packedchars_n += range.num_chars; - } + stbrp_pack_rects((stbrp_context*)spc.pack_info, src_tmp.Rects, src_tmp.GlyphsCount); - // Gather the sizes of all rectangle we need - tmp.Rects = buf_rects + buf_rects_n; - tmp.RectsCount = font_glyphs_count; - buf_rects_n += font_glyphs_count; - stbtt_PackSetOversampling(&spc, cfg.OversampleH, cfg.OversampleV); - int n = stbtt_PackFontRangesGatherRects(&spc, &tmp.FontInfo, tmp.Ranges, tmp.RangesCount, tmp.Rects); - IM_ASSERT(n == font_glyphs_count); - - // Detect missing glyphs and replace them with a zero-sized box instead of relying on the default glyphs - // This allows us merging overlapping icon fonts more easily. - int rect_i = 0; - for (int range_i = 0; range_i < tmp.RangesCount; range_i++) - for (int char_i = 0; char_i < tmp.Ranges[range_i].num_chars; char_i++, rect_i++) - if (stbtt_FindGlyphIndex(&tmp.FontInfo, tmp.Ranges[range_i].first_unicode_codepoint_in_range + char_i) == 0) - tmp.Rects[rect_i].w = tmp.Rects[rect_i].h = 0; - - // Pack - stbrp_pack_rects((stbrp_context*)spc.pack_info, tmp.Rects, n); - - // Extend texture height - // Also mark missing glyphs as non-packed so we don't attempt to render into them - for (int i = 0; i < n; i++) - { - if (tmp.Rects[i].w == 0 && tmp.Rects[i].h == 0) - tmp.Rects[i].was_packed = 0; - if (tmp.Rects[i].was_packed) - atlas->TexHeight = ImMax(atlas->TexHeight, tmp.Rects[i].y + tmp.Rects[i].h); - } + // Extend texture height and mark missing glyphs as non-packed so we won't render them. + // FIXME: We are not handling packing failure here (would happen if we got off TEX_HEIGHT_MAX or if a single if larger than TexWidth?) + for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++) + if (src_tmp.Rects[glyph_i].was_packed) + atlas->TexHeight = ImMax(atlas->TexHeight, src_tmp.Rects[glyph_i].y + src_tmp.Rects[glyph_i].h); } - IM_ASSERT(buf_rects_n == total_glyphs_count); - IM_ASSERT(buf_packedchars_n == total_glyphs_count); - IM_ASSERT(buf_ranges_n == total_ranges_count); - // Create texture + // 7. Allocate texture atlas->TexHeight = (atlas->Flags & ImFontAtlasFlags_NoPowerOfTwoHeight) ? (atlas->TexHeight + 1) : ImUpperPowerOfTwo(atlas->TexHeight); atlas->TexUvScale = ImVec2(1.0f / atlas->TexWidth, 1.0f / atlas->TexHeight); atlas->TexPixelsAlpha8 = (unsigned char*)ImGui::MemAlloc(atlas->TexWidth * atlas->TexHeight); @@ -1850,41 +1969,46 @@ bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) spc.pixels = atlas->TexPixelsAlpha8; spc.height = atlas->TexHeight; - // Second pass: render font characters - for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++) + // 8. Render/rasterize font characters into the texture + for (int src_i = 0; src_i < src_tmp_array.Size; src_i++) { - ImFontConfig& cfg = atlas->ConfigData[input_i]; - ImFontTempBuildData& tmp = tmp_array[input_i]; - stbtt_PackSetOversampling(&spc, cfg.OversampleH, cfg.OversampleV); - stbtt_PackFontRangesRenderIntoRects(&spc, &tmp.FontInfo, tmp.Ranges, tmp.RangesCount, tmp.Rects); + ImFontConfig& cfg = atlas->ConfigData[src_i]; + ImFontBuildSrcData& src_tmp = src_tmp_array[src_i]; + if (src_tmp.GlyphsCount == 0) + continue; + + stbtt_PackFontRangesRenderIntoRects(&spc, &src_tmp.FontInfo, &src_tmp.PackRange, 1, src_tmp.Rects); + + // Apply multiply operator if (cfg.RasterizerMultiply != 1.0f) { unsigned char multiply_table[256]; ImFontAtlasBuildMultiplyCalcLookupTable(multiply_table, cfg.RasterizerMultiply); - for (const stbrp_rect* r = tmp.Rects; r != tmp.Rects + tmp.RectsCount; r++) + stbrp_rect* r = &src_tmp.Rects[0]; + for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++, r++) if (r->was_packed) - ImFontAtlasBuildMultiplyRectAlpha8(multiply_table, spc.pixels, r->x, r->y, r->w, r->h, spc.stride_in_bytes); + ImFontAtlasBuildMultiplyRectAlpha8(multiply_table, atlas->TexPixelsAlpha8, r->x, r->y, r->w, r->h, atlas->TexWidth * 1); } - tmp.Rects = NULL; + src_tmp.Rects = NULL; } // End packing stbtt_PackEnd(&spc); - ImGui::MemFree(buf_rects); - buf_rects = NULL; + buf_rects.clear(); - // Third pass: setup ImFont and glyphs for runtime - for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++) + // 9. Setup ImFont and glyphs for runtime + for (int src_i = 0; src_i < src_tmp_array.Size; src_i++) { - ImFontConfig& cfg = atlas->ConfigData[input_i]; - ImFontTempBuildData& tmp = tmp_array[input_i]; + ImFontBuildSrcData& src_tmp = src_tmp_array[src_i]; + if (src_tmp.GlyphsCount == 0) + continue; + + ImFontConfig& cfg = atlas->ConfigData[src_i]; ImFont* dst_font = cfg.DstFont; // We can have multiple input fonts writing into a same destination font (when using MergeMode=true) - if (cfg.MergeMode) - dst_font->BuildLookupTable(); - const float font_scale = stbtt_ScaleForPixelHeight(&tmp.FontInfo, cfg.SizePixels); + const float font_scale = stbtt_ScaleForPixelHeight(&src_tmp.FontInfo, cfg.SizePixels); int unscaled_ascent, unscaled_descent, unscaled_line_gap; - stbtt_GetFontVMetrics(&tmp.FontInfo, &unscaled_ascent, &unscaled_descent, &unscaled_line_gap); + stbtt_GetFontVMetrics(&src_tmp.FontInfo, &unscaled_ascent, &unscaled_descent, &unscaled_line_gap); const float ascent = ImFloor(unscaled_ascent * font_scale + ((unscaled_ascent > 0.0f) ? +1 : -1)); const float descent = ImFloor(unscaled_descent * font_scale + ((unscaled_descent > 0.0f) ? +1 : -1)); @@ -1892,40 +2016,30 @@ bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) const float font_off_x = cfg.GlyphOffset.x; const float font_off_y = cfg.GlyphOffset.y + (float)(int)(dst_font->Ascent + 0.5f); - for (int i = 0; i < tmp.RangesCount; i++) + for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++) { - stbtt_pack_range& range = tmp.Ranges[i]; - for (int char_idx = 0; char_idx < range.num_chars; char_idx += 1) - { - const stbtt_packedchar& pc = range.chardata_for_range[char_idx]; - if (!pc.x0 && !pc.x1 && !pc.y0 && !pc.y1) - continue; - - const int codepoint = range.first_unicode_codepoint_in_range + char_idx; - if (cfg.MergeMode && dst_font->FindGlyphNoFallback((ImWchar)codepoint)) - continue; - - float char_advance_x_org = pc.xadvance; - float char_advance_x_mod = ImClamp(char_advance_x_org, cfg.GlyphMinAdvanceX, cfg.GlyphMaxAdvanceX); - float char_off_x = font_off_x; - if (char_advance_x_org != char_advance_x_mod) - char_off_x += cfg.PixelSnapH ? (float)(int)((char_advance_x_mod - char_advance_x_org) * 0.5f) : (char_advance_x_mod - char_advance_x_org) * 0.5f; - - stbtt_aligned_quad q; - float dummy_x = 0.0f, dummy_y = 0.0f; - stbtt_GetPackedQuad(range.chardata_for_range, atlas->TexWidth, atlas->TexHeight, char_idx, &dummy_x, &dummy_y, &q, 0); - dst_font->AddGlyph((ImWchar)codepoint, q.x0 + char_off_x, q.y0 + font_off_y, q.x1 + char_off_x, q.y1 + font_off_y, q.s0, q.t0, q.s1, q.t1, char_advance_x_mod); - } + const int codepoint = src_tmp.GlyphsList[glyph_i]; + const stbtt_packedchar& pc = src_tmp.PackedChars[glyph_i]; + + const float char_advance_x_org = pc.xadvance; + const float char_advance_x_mod = ImClamp(char_advance_x_org, cfg.GlyphMinAdvanceX, cfg.GlyphMaxAdvanceX); + float char_off_x = font_off_x; + if (char_advance_x_org != char_advance_x_mod) + char_off_x += cfg.PixelSnapH ? (float)(int)((char_advance_x_mod - char_advance_x_org) * 0.5f) : (char_advance_x_mod - char_advance_x_org) * 0.5f; + + // Register glyph + stbtt_aligned_quad q; + float dummy_x = 0.0f, dummy_y = 0.0f; + stbtt_GetPackedQuad(src_tmp.PackedChars, atlas->TexWidth, atlas->TexHeight, glyph_i, &dummy_x, &dummy_y, &q, 0); + dst_font->AddGlyph((ImWchar)codepoint, q.x0 + char_off_x, q.y0 + font_off_y, q.x1 + char_off_x, q.y1 + font_off_y, q.s0, q.t0, q.s1, q.t1, char_advance_x_mod); } } - // Cleanup temporaries - ImGui::MemFree(buf_packedchars); - ImGui::MemFree(buf_ranges); - ImGui::MemFree(tmp_array); + // Cleanup temporary (ImVector doesn't honor destructor) + for (int src_i = 0; src_i < src_tmp_array.Size; src_i++) + src_tmp_array[src_i].~ImFontBuildSrcData(); ImFontAtlasBuildFinish(atlas); - return true; } @@ -1953,16 +2067,17 @@ void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* f font->ConfigDataCount++; } -void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* pack_context_opaque) +void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opaque) { - stbrp_context* pack_context = (stbrp_context*)pack_context_opaque; + stbrp_context* pack_context = (stbrp_context*)stbrp_context_opaque; + IM_ASSERT(pack_context != NULL); ImVector& user_rects = atlas->CustomRects; IM_ASSERT(user_rects.Size >= 1); // We expect at least the default custom rects to be registered, else something went wrong. ImVector pack_rects; pack_rects.resize(user_rects.Size); - memset(pack_rects.Data, 0, sizeof(stbrp_rect) * user_rects.Size); + memset(pack_rects.Data, 0, (size_t)pack_rects.size_in_bytes()); for (int i = 0; i < user_rects.Size; i++) { pack_rects[i].w = user_rects[i].Width; @@ -2062,7 +2177,8 @@ const ImWchar* ImFontAtlas::GetGlyphRangesChineseFull() static const ImWchar ranges[] = { 0x0020, 0x00FF, // Basic Latin + Latin Supplement - 0x3000, 0x30FF, // Punctuations, Hiragana, Katakana + 0x2000, 0x206F, // General Punctuation + 0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana 0x31F0, 0x31FF, // Katakana Phonetic Extensions 0xFF00, 0xFFEF, // Half-width characters 0x4e00, 0x9FAF, // CJK Ideograms @@ -2082,7 +2198,7 @@ static void UnpackAccumulativeOffsetsIntoRanges(int base_codepoint, const short* } //------------------------------------------------------------------------- -// [SECTION] ImFontAtlas glyph ranges helpers + GlyphRangesBuilder +// [SECTION] ImFontAtlas glyph ranges helpers //------------------------------------------------------------------------- const ImWchar* ImFontAtlas::GetGlyphRangesChineseSimplifiedCommon() @@ -2090,7 +2206,7 @@ const ImWchar* ImFontAtlas::GetGlyphRangesChineseSimplifiedCommon() // Store 2500 regularly used characters for Simplified Chinese. // Sourced from https://zh.wiktionary.org/wiki/%E9%99%84%E5%BD%95:%E7%8E%B0%E4%BB%A3%E6%B1%89%E8%AF%AD%E5%B8%B8%E7%94%A8%E5%AD%97%E8%A1%A8 // This table covers 97.97% of all characters used during the month in July, 1987. - // You can use ImFontAtlas::GlyphRangesBuilder to create your own ranges derived from this, by merging existing ranges or adding new characters. + // You can use ImFontGlyphRangesBuilder to create your own ranges derived from this, by merging existing ranges or adding new characters. // (Stored as accumulative offsets from the initial unicode codepoint 0x4E00. This encoding is designed to helps us compact the source code size.) static const short accumulative_offsets_from_0x4E00[] = { @@ -2138,9 +2254,10 @@ const ImWchar* ImFontAtlas::GetGlyphRangesChineseSimplifiedCommon() static ImWchar base_ranges[] = // not zero-terminated { 0x0020, 0x00FF, // Basic Latin + Latin Supplement - 0x3000, 0x30FF, // Punctuations, Hiragana, Katakana + 0x2000, 0x206F, // General Punctuation + 0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana 0x31F0, 0x31FF, // Katakana Phonetic Extensions - 0xFF00, 0xFFEF, // Half-width characters + 0xFF00, 0xFFEF // Half-width characters }; static ImWchar full_ranges[IM_ARRAYSIZE(base_ranges) + IM_ARRAYSIZE(accumulative_offsets_from_0x4E00) * 2 + 1] = { 0 }; if (!full_ranges[0]) @@ -2156,7 +2273,7 @@ const ImWchar* ImFontAtlas::GetGlyphRangesJapanese() // 1946 common ideograms code points for Japanese // Sourced from http://theinstructionlimit.com/common-kanji-character-ranges-for-xna-spritefont-rendering // FIXME: Source a list of the revised 2136 Joyo Kanji list from 2010 and rebuild this. - // You can use ImFontAtlas::GlyphRangesBuilder to create your own ranges derived from this, by merging existing ranges or adding new characters. + // You can use ImFontGlyphRangesBuilder to create your own ranges derived from this, by merging existing ranges or adding new characters. // (Stored as accumulative offsets from the initial unicode codepoint 0x4E00. This encoding is designed to helps us compact the source code size.) static const short accumulative_offsets_from_0x4E00[] = { @@ -2191,14 +2308,14 @@ const ImWchar* ImFontAtlas::GetGlyphRangesJapanese() 3,7,6,3,1,2,3,9,1,3,1,6,3,2,1,3,11,3,1,6,10,3,2,3,1,2,1,5,1,1,11,3,6,4,1,7,2,1,2,5,5,34,4,14,18,4,19,7,5,8,2,6,79,1,5,2,14,8,2,9,2,1,36,28,16, 4,1,1,1,2,12,6,42,39,16,23,7,15,15,3,2,12,7,21,64,6,9,28,8,12,3,3,41,59,24,51,55,57,294,9,9,2,6,2,15,1,2,13,38,90,9,9,9,3,11,7,1,1,1,5,6,3,2, 1,2,2,3,8,1,4,4,1,5,7,1,4,3,20,4,9,1,1,1,5,5,17,1,5,2,6,2,4,1,4,5,7,3,18,11,11,32,7,5,4,7,11,127,8,4,3,3,1,10,1,1,6,21,14,1,16,1,7,1,3,6,9,65, - 51,4,3,13,3,10,1,1,12,9,21,110,3,19,24,1,1,10,62,4,1,29,42,78,28,20,18,82,6,3,15,6,84,58,253,15,155,264,15,21,9,14,7,58,40,39, + 51,4,3,13,3,10,1,1,12,9,21,110,3,19,24,1,1,10,62,4,1,29,42,78,28,20,18,82,6,3,15,6,84,58,253,15,155,264,15,21,9,14,7,58,40,39, }; static ImWchar base_ranges[] = // not zero-terminated { 0x0020, 0x00FF, // Basic Latin + Latin Supplement - 0x3000, 0x30FF, // Punctuations, Hiragana, Katakana + 0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana 0x31F0, 0x31FF, // Katakana Phonetic Extensions - 0xFF00, 0xFFEF, // Half-width characters + 0xFF00, 0xFFEF // Half-width characters }; static ImWchar full_ranges[IM_ARRAYSIZE(base_ranges) + IM_ARRAYSIZE(accumulative_offsets_from_0x4E00)*2 + 1] = { 0 }; if (!full_ranges[0]) @@ -2234,7 +2351,28 @@ const ImWchar* ImFontAtlas::GetGlyphRangesThai() return &ranges[0]; } -void ImFontAtlas::GlyphRangesBuilder::AddText(const char* text, const char* text_end) +const ImWchar* ImFontAtlas::GetGlyphRangesVietnamese() +{ + static const ImWchar ranges[] = + { + 0x0020, 0x00FF, // Basic Latin + 0x0102, 0x0103, + 0x0110, 0x0111, + 0x0128, 0x0129, + 0x0168, 0x0169, + 0x01A0, 0x01A1, + 0x01AF, 0x01B0, + 0x1EA0, 0x1EF9, + 0, + }; + return &ranges[0]; +} + +//----------------------------------------------------------------------------- +// [SECTION] ImFontGlyphRangesBuilder +//----------------------------------------------------------------------------- + +void ImFontGlyphRangesBuilder::AddText(const char* text, const char* text_end) { while (text_end ? (text < text_end) : *text) { @@ -2248,14 +2386,14 @@ void ImFontAtlas::GlyphRangesBuilder::AddText(const char* text, const char* text } } -void ImFontAtlas::GlyphRangesBuilder::AddRanges(const ImWchar* ranges) +void ImFontGlyphRangesBuilder::AddRanges(const ImWchar* ranges) { for (; ranges[0]; ranges += 2) for (ImWchar c = ranges[0]; c <= ranges[1]; c++) AddChar(c); } -void ImFontAtlas::GlyphRangesBuilder::BuildRanges(ImVector* out_ranges) +void ImFontGlyphRangesBuilder::BuildRanges(ImVector* out_ranges) { for (int n = 0; n < 0x10000; n++) if (GetBit(n)) @@ -2274,38 +2412,36 @@ void ImFontAtlas::GlyphRangesBuilder::BuildRanges(ImVector* out_ranges) ImFont::ImFont() { - Scale = 1.0f; + FontSize = 0.0f; + FallbackAdvanceX = 0.0f; FallbackChar = (ImWchar)'?'; DisplayOffset = ImVec2(0.0f, 0.0f); - ClearOutputData(); + FallbackGlyph = NULL; + ContainerAtlas = NULL; + ConfigData = NULL; + ConfigDataCount = 0; + DirtyLookupTables = false; + Scale = 1.0f; + Ascent = Descent = 0.0f; + MetricsTotalSurface = 0; } ImFont::~ImFont() { - // Invalidate active font so that the user gets a clear crash instead of a dangling pointer. - // If you want to delete fonts you need to do it between Render() and NewFrame(). - // FIXME-CLEANUP - /* - ImGuiContext& g = *GImGui; - if (g.Font == this) - g.Font = NULL; - */ ClearOutputData(); } void ImFont::ClearOutputData() { FontSize = 0.0f; + FallbackAdvanceX = 0.0f; Glyphs.clear(); IndexAdvanceX.clear(); IndexLookup.clear(); FallbackGlyph = NULL; - FallbackAdvanceX = 0.0f; - ConfigDataCount = 0; - ConfigData = NULL; ContainerAtlas = NULL; - Ascent = Descent = 0.0f; DirtyLookupTables = true; + Ascent = Descent = 0.0f; MetricsTotalSurface = 0; } @@ -2407,7 +2543,7 @@ const ImFontGlyph* ImFont::FindGlyph(ImWchar c) const { if (c >= IndexLookup.Size) return FallbackGlyph; - const ImWchar i = IndexLookup[c]; + const ImWchar i = IndexLookup.Data[c]; if (i == (ImWchar)-1) return FallbackGlyph; return &Glyphs.Data[i]; @@ -2417,7 +2553,7 @@ const ImFontGlyph* ImFont::FindGlyphNoFallback(ImWchar c) const { if (c >= IndexLookup.Size) return NULL; - const ImWchar i = IndexLookup[c]; + const ImWchar i = IndexLookup.Data[c]; if (i == (ImWchar)-1) return NULL; return &Glyphs.Data[i]; @@ -2477,7 +2613,7 @@ const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const c } } - const float char_width = ((int)c < IndexAdvanceX.Size ? IndexAdvanceX[(int)c] : FallbackAdvanceX); + const float char_width = ((int)c < IndexAdvanceX.Size ? IndexAdvanceX.Data[c] : FallbackAdvanceX); if (ImCharIsBlankW(c)) { if (inside_word) @@ -2594,7 +2730,7 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons continue; } - const float char_width = ((int)c < IndexAdvanceX.Size ? IndexAdvanceX[(int)c] : FallbackAdvanceX) * scale; + const float char_width = ((int)c < IndexAdvanceX.Size ? IndexAdvanceX.Data[c] : FallbackAdvanceX) * scale; if (line_width + char_width >= max_width) { s = prev_s; @@ -2667,7 +2803,7 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col while (y_end < clip_rect.w && s_end < text_end) { s_end = (const char*)memchr(s_end, '\n', text_end - s_end); - s = s ? s + 1 : text_end; + s_end = s_end ? s_end + 1 : text_end; y_end += line_height; } text_end = s_end; @@ -2826,6 +2962,7 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col // - RenderMouseCursor() // - RenderArrowPointingAt() // - RenderRectFilledRangeH() +// - RenderPixelEllipsis() //----------------------------------------------------------------------------- void ImGui::RenderMouseCursor(ImDrawList* draw_list, ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor) @@ -2894,13 +3031,14 @@ void ImGui::RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, Im const float inv_rounding = 1.0f / rounding; const float arc0_b = ImAcos01(1.0f - (p0.x - rect.Min.x) * inv_rounding); const float arc0_e = ImAcos01(1.0f - (p1.x - rect.Min.x) * inv_rounding); + const float half_pi = IM_PI * 0.5f; // We will == compare to this because we know this is the exact value ImAcos01 can return. const float x0 = ImMax(p0.x, rect.Min.x + rounding); if (arc0_b == arc0_e) { draw_list->PathLineTo(ImVec2(x0, p1.y)); draw_list->PathLineTo(ImVec2(x0, p0.y)); } - else if (arc0_b == 0.0f && arc0_e == IM_PI*0.5f) + else if (arc0_b == 0.0f && arc0_e == half_pi) { draw_list->PathArcToFast(ImVec2(x0, p1.y - rounding), rounding, 3, 6); // BL draw_list->PathArcToFast(ImVec2(x0, p0.y + rounding), rounding, 6, 9); // TR @@ -2920,7 +3058,7 @@ void ImGui::RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, Im draw_list->PathLineTo(ImVec2(x1, p0.y)); draw_list->PathLineTo(ImVec2(x1, p1.y)); } - else if (arc1_b == 0.0f && arc1_e == IM_PI*0.5f) + else if (arc1_b == 0.0f && arc1_e == half_pi) { draw_list->PathArcToFast(ImVec2(x1, p0.y + rounding), rounding, 9, 12); // TR draw_list->PathArcToFast(ImVec2(x1, p1.y - rounding), rounding, 0, 3); // BR @@ -2934,6 +3072,17 @@ void ImGui::RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, Im draw_list->PathFillConvex(col); } +// FIXME: Rendering an ellipsis "..." is a surprisingly tricky problem for us... we cannot rely on font glyph having it, +// and regular dot are typically too wide. If we render a dot/shape ourselves it comes with the risk that it wouldn't match +// the boldness or positioning of what the font uses... +void ImGui::RenderPixelEllipsis(ImDrawList* draw_list, ImVec2 pos, int count, ImU32 col) +{ + ImFont* font = draw_list->_Data->Font; + const float font_scale = draw_list->_Data->FontSize / font->FontSize; + pos.y += (float)(int)(font->DisplayOffset.y + font->Ascent * font_scale + 0.5f - 1.0f); + for (int dot_n = 0; dot_n < count; dot_n++) + draw_list->AddRectFilled(ImVec2(pos.x + dot_n * 2.0f, pos.y), ImVec2(pos.x + dot_n * 2.0f + 1.0f, pos.y + 1.0f), col); +} //----------------------------------------------------------------------------- // [SECTION] Decompression code diff --git a/src/lib/geogram_gfx/third_party/ImGui/imgui_impl_glfw.cpp b/src/lib/geogram_gfx/third_party/ImGui/imgui_impl_glfw.cpp index 4385bbe9..761789c2 100644 --- a/src/lib/geogram_gfx/third_party/ImGui/imgui_impl_glfw.cpp +++ b/src/lib/geogram_gfx/third_party/ImGui/imgui_impl_glfw.cpp @@ -1,6 +1,7 @@ // dear imgui: Platform Binding for GLFW // This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan..) // (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) +// (Requires: GLFW 3.1+) // Implemented features: // [X] Platform: Clipboard support. @@ -14,6 +15,8 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2019-03-12: Misc: Preserve DisplayFramebufferScale when main window is minimized. +// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window. // 2018-11-07: Inputs: When installing our GLFW callbacks, we save user's previously installed ones - if any - and chain call them. // 2018-08-01: Inputs: Workaround for Emscripten which doesn't seem to handle focus related calls. // 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor. @@ -29,13 +32,13 @@ // 2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1). // 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers. -#ifndef __ANDROID__ /* [Bruno] */ +#ifndef __ANDROID__ // [Bruno Levy] #include "imgui.h" #include "imgui_impl_glfw.h" // GLFW -// [Bruno] adapted to geogram glfw compile +// [Bruno Levy] adapted to geogram glfw compile #ifdef __EMSCRIPTEN__ #include #include @@ -48,7 +51,7 @@ #endif // Win32 / glfwGetWin32Window -// [Bruno] adapted to geogram glfw compile +// [Bruno Levy] adapted to geogram glfw compile #ifdef _WIN32 #undef APIENTRY #define GLFW_EXPOSE_NATIVE_WIN32 @@ -59,7 +62,6 @@ #endif #endif - #define GLFW_HAS_WINDOW_TOPMOST (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ GLFW_FLOATING #define GLFW_HAS_WINDOW_HOVERED (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ GLFW_HOVERED #define GLFW_HAS_WINDOW_ALPHA (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwSetWindowOpacity @@ -129,7 +131,7 @@ void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yo void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) { - if (g_PrevUserCallbackKey != NULL) + if (g_PrevUserCallbackKey != NULL) g_PrevUserCallbackKey(window, key, scancode, action, mods); ImGuiIO& io = ImGui::GetIO(); @@ -164,6 +166,7 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw ImGuiIO& io = ImGui::GetIO(); io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) + io.BackendPlatformName = "imgui_impl_glfw"; // Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array. io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB; @@ -208,7 +211,7 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw g_MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); // FIXME: GLFW doesn't have this. g_MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); // FIXME: GLFW doesn't have this. g_MouseCursors[ImGuiMouseCursor_Hand] = glfwCreateStandardCursor(GLFW_HAND_CURSOR); - + // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. g_PrevUserCallbackMousebutton = NULL; g_PrevUserCallbackScroll = NULL; @@ -301,18 +304,56 @@ static void ImGui_ImplGlfw_UpdateMouseCursor() } } +static void ImGui_ImplGlfw_UpdateGamepads() +{ + ImGuiIO& io = ImGui::GetIO(); + memset(io.NavInputs, 0, sizeof(io.NavInputs)); + if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) + return; + + // Update gamepad inputs + #define MAP_BUTTON(NAV_NO, BUTTON_NO) { if (buttons_count > BUTTON_NO && buttons[BUTTON_NO] == GLFW_PRESS) io.NavInputs[NAV_NO] = 1.0f; } + #define MAP_ANALOG(NAV_NO, AXIS_NO, V0, V1) { float v = (axes_count > AXIS_NO) ? axes[AXIS_NO] : V0; v = (v - V0) / (V1 - V0); if (v > 1.0f) v = 1.0f; if (io.NavInputs[NAV_NO] < v) io.NavInputs[NAV_NO] = v; } + int axes_count = 0, buttons_count = 0; + const float* axes = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &axes_count); + const unsigned char* buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_1, &buttons_count); + MAP_BUTTON(ImGuiNavInput_Activate, 0); // Cross / A + MAP_BUTTON(ImGuiNavInput_Cancel, 1); // Circle / B + MAP_BUTTON(ImGuiNavInput_Menu, 2); // Square / X + MAP_BUTTON(ImGuiNavInput_Input, 3); // Triangle / Y + MAP_BUTTON(ImGuiNavInput_DpadLeft, 13); // D-Pad Left + MAP_BUTTON(ImGuiNavInput_DpadRight, 11); // D-Pad Right + MAP_BUTTON(ImGuiNavInput_DpadUp, 10); // D-Pad Up + MAP_BUTTON(ImGuiNavInput_DpadDown, 12); // D-Pad Down + MAP_BUTTON(ImGuiNavInput_FocusPrev, 4); // L1 / LB + MAP_BUTTON(ImGuiNavInput_FocusNext, 5); // R1 / RB + MAP_BUTTON(ImGuiNavInput_TweakSlow, 4); // L1 / LB + MAP_BUTTON(ImGuiNavInput_TweakFast, 5); // R1 / RB + MAP_ANALOG(ImGuiNavInput_LStickLeft, 0, -0.3f, -0.9f); + MAP_ANALOG(ImGuiNavInput_LStickRight,0, +0.3f, +0.9f); + MAP_ANALOG(ImGuiNavInput_LStickUp, 1, +0.3f, +0.9f); + MAP_ANALOG(ImGuiNavInput_LStickDown, 1, -0.3f, -0.9f); + #undef MAP_BUTTON + #undef MAP_ANALOG + if (axes_count > 0 && buttons_count > 0) + io.BackendFlags |= ImGuiBackendFlags_HasGamepad; + else + io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; +} + void ImGui_ImplGlfw_NewFrame() { ImGuiIO& io = ImGui::GetIO(); - IM_ASSERT(io.Fonts->IsBuilt()); // Font atlas needs to be built, call renderer _NewFrame() function e.g. ImGui_ImplOpenGL3_NewFrame() + IM_ASSERT(io.Fonts->IsBuilt() && "Font atlas not built! It is generally built by the renderer back-end. Missing call to renderer _NewFrame() function? e.g. ImGui_ImplOpenGL3_NewFrame()."); - // Setup display size + // Setup display size (every frame to accommodate for window resizing) int w, h; int display_w, display_h; glfwGetWindowSize(g_Window, &w, &h); glfwGetFramebufferSize(g_Window, &display_w, &display_h); io.DisplaySize = ImVec2((float)w, (float)h); - io.DisplayFramebufferScale = ImVec2(w > 0 ? ((float)display_w / w) : 0, h > 0 ? ((float)display_h / h) : 0); + if (w > 0 && h > 0) + io.DisplayFramebufferScale = ImVec2((float)display_w / w, (float)display_h / h); // Setup time step double current_time = glfwGetTime(); @@ -322,39 +363,8 @@ void ImGui_ImplGlfw_NewFrame() ImGui_ImplGlfw_UpdateMousePosAndButtons(); ImGui_ImplGlfw_UpdateMouseCursor(); - // Gamepad navigation mapping [BETA] - memset(io.NavInputs, 0, sizeof(io.NavInputs)); - if (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) - { - // Update gamepad inputs - #define MAP_BUTTON(NAV_NO, BUTTON_NO) { if (buttons_count > BUTTON_NO && buttons[BUTTON_NO] == GLFW_PRESS) io.NavInputs[NAV_NO] = 1.0f; } - #define MAP_ANALOG(NAV_NO, AXIS_NO, V0, V1) { float v = (axes_count > AXIS_NO) ? axes[AXIS_NO] : V0; v = (v - V0) / (V1 - V0); if (v > 1.0f) v = 1.0f; if (io.NavInputs[NAV_NO] < v) io.NavInputs[NAV_NO] = v; } - int axes_count = 0, buttons_count = 0; - const float* axes = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &axes_count); - const unsigned char* buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_1, &buttons_count); - MAP_BUTTON(ImGuiNavInput_Activate, 0); // Cross / A - MAP_BUTTON(ImGuiNavInput_Cancel, 1); // Circle / B - MAP_BUTTON(ImGuiNavInput_Menu, 2); // Square / X - MAP_BUTTON(ImGuiNavInput_Input, 3); // Triangle / Y - MAP_BUTTON(ImGuiNavInput_DpadLeft, 13); // D-Pad Left - MAP_BUTTON(ImGuiNavInput_DpadRight, 11); // D-Pad Right - MAP_BUTTON(ImGuiNavInput_DpadUp, 10); // D-Pad Up - MAP_BUTTON(ImGuiNavInput_DpadDown, 12); // D-Pad Down - MAP_BUTTON(ImGuiNavInput_FocusPrev, 4); // L1 / LB - MAP_BUTTON(ImGuiNavInput_FocusNext, 5); // R1 / RB - MAP_BUTTON(ImGuiNavInput_TweakSlow, 4); // L1 / LB - MAP_BUTTON(ImGuiNavInput_TweakFast, 5); // R1 / RB - MAP_ANALOG(ImGuiNavInput_LStickLeft, 0, -0.3f, -0.9f); - MAP_ANALOG(ImGuiNavInput_LStickRight,0, +0.3f, +0.9f); - MAP_ANALOG(ImGuiNavInput_LStickUp, 1, +0.3f, +0.9f); - MAP_ANALOG(ImGuiNavInput_LStickDown, 1, -0.3f, -0.9f); - #undef MAP_BUTTON - #undef MAP_ANALOG - if (axes_count > 0 && buttons_count > 0) - io.BackendFlags |= ImGuiBackendFlags_HasGamepad; - else - io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; - } + // Gamepad navigation mapping + ImGui_ImplGlfw_UpdateGamepads(); } -#endif /* __ANDROID__ [Bruno] */ +#endif // __ANDROID__ [Bruno Levy] diff --git a/src/lib/geogram_gfx/third_party/ImGui/imgui_impl_glfw.h b/src/lib/geogram_gfx/third_party/ImGui/imgui_impl_glfw.h index 6b606c8c..d01ff8b2 100644 --- a/src/lib/geogram_gfx/third_party/ImGui/imgui_impl_glfw.h +++ b/src/lib/geogram_gfx/third_party/ImGui/imgui_impl_glfw.h @@ -2,29 +2,29 @@ * dear imgui: Platform Binding for GLFW * This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan..) * (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) - * + * Implemented features: * [X] Platform: Clipboard support. * [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. * [x] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: 3 cursors types are missing from GLFW. * [X] Platform: Keyboard arrays indexed using GLFW_KEY_* codes, e.g. ImGui::IsKeyPressed(GLFW_KEY_SPACE). - * + * You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. * If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp. * https:*github.com/ocornut/imgui - * + * About GLSL version: * The 'glsl_version' initialization parameter defaults to "#version 150" if NULL. * Only override if your GL version doesn't handle this GLSL version. Keep NULL if unsure! */ -/* [Bruno] C-style comment */ +/* [Bruno Levy] C-style comment */ #pragma once -#ifndef __ANDROID__ /* [Bruno] */ +#ifndef __ANDROID__ /* [Bruno Levy] */ -/* [Bruno] */ +/* [Bruno Levy] */ #ifdef __cplusplus extern "C" { #endif @@ -45,10 +45,11 @@ IMGUI_IMPL_API void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double IMGUI_IMPL_API void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods); IMGUI_IMPL_API void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c); -/* [Bruno] */ +/* [Bruno Levy] */ #ifdef __cplusplus } #endif -#endif /* __ANDROID__ [Bruno] */ +#endif /* __ANDROID__ [Bruno Levy] */ + diff --git a/src/lib/geogram_gfx/third_party/ImGui/imgui_impl_opengl2.cpp b/src/lib/geogram_gfx/third_party/ImGui/imgui_impl_opengl2.cpp deleted file mode 100644 index edfcd4c9..00000000 --- a/src/lib/geogram_gfx/third_party/ImGui/imgui_impl_opengl2.cpp +++ /dev/null @@ -1,232 +0,0 @@ -// dear imgui: Renderer for OpenGL2 (legacy OpenGL, fixed pipeline) -// This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..) - -// Implemented features: -// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. - -// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. -// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp. -// https://github.com/ocornut/imgui - -// **DO NOT USE THIS CODE IF YOUR CODE/ENGINE IS USING MODERN OPENGL (SHADERS, VBO, VAO, etc.)** -// **Prefer using the code in imgui_impl_opengl3.cpp** -// This code is mostly provided as a reference to learn how ImGui integration works, because it is shorter to read. -// If your code is using GL3+ context or any semi modern OpenGL calls, using this is likely to make everything more -// complicated, will require your code to reset every single OpenGL attributes to their initial state, and might -// confuse your GPU driver. -// The GL2 code is unable to reset attributes or even call e.g. "glUseProgram(0)" because they don't exist in that API. - -// CHANGELOG -// (minor and older changes stripped away, please see git history for details) -// 2018-08-03: OpenGL: Disabling/restoring GL_LIGHTING and GL_COLOR_MATERIAL to increase compatibility with legacy OpenGL applications. -// 2018-06-08: Misc: Extracted imgui_impl_opengl2.cpp/.h away from the old combined GLFW/SDL+OpenGL2 examples. -// 2018-06-08: OpenGL: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle. -// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplGlfwGL2_RenderDrawData() in the .h file so you can call it yourself. -// 2017-09-01: OpenGL: Save and restore current polygon mode. -// 2016-09-10: OpenGL: Uploading font texture as RGBA32 to increase compatibility with users shaders (not ideal). -// 2016-09-05: OpenGL: Fixed save and restore of current scissor rectangle. - -#include "imgui.h" -#include "imgui_impl_opengl2.h" - -// [Bruno 05/16/2016] conditional compilation -#ifdef GEO_GL_LEGACY - -#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier -#include // intptr_t -#else -#include // intptr_t -#endif - -// Include OpenGL header (without an OpenGL loader) requires a bit of fiddling -#if defined(_WIN32) && !defined(APIENTRY) -#define APIENTRY __stdcall // It is customary to use APIENTRY for OpenGL function pointer declarations on all platforms. Additionally, the Windows OpenGL header needs APIENTRY. -#endif -#if defined(_WIN32) && !defined(WINGDIAPI) -#define WINGDIAPI __declspec(dllimport) // Some Windows OpenGL headers need this -#endif -#if defined(__APPLE__) -#include -#else -#include -#endif - -// OpenGL Data -static GLuint g_FontTexture = 0; - -// Functions -bool ImGui_ImplOpenGL2_Init() -{ - return true; -} - -void ImGui_ImplOpenGL2_Shutdown() -{ - ImGui_ImplOpenGL2_DestroyDeviceObjects(); -} - -void ImGui_ImplOpenGL2_NewFrame() -{ - if (!g_FontTexture) - ImGui_ImplOpenGL2_CreateDeviceObjects(); -} - -// OpenGL2 Render function. -// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop) -// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly, in order to be able to run within any OpenGL engine that doesn't do so. -void ImGui_ImplOpenGL2_RenderDrawData(ImDrawData* draw_data) -{ - // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) - ImGuiIO& io = ImGui::GetIO(); - int fb_width = (int)(draw_data->DisplaySize.x * io.DisplayFramebufferScale.x); - int fb_height = (int)(draw_data->DisplaySize.y * io.DisplayFramebufferScale.y); - if (fb_width == 0 || fb_height == 0) - return; - draw_data->ScaleClipRects(io.DisplayFramebufferScale); - - // We are using the OpenGL fixed pipeline to make the example code simpler to read! - // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, vertex/texcoord/color pointers, polygon fill. - GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode); - GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport); - GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box); - glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_TRANSFORM_BIT); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDisable(GL_CULL_FACE); - glDisable(GL_DEPTH_TEST); - glDisable(GL_LIGHTING); - glDisable(GL_COLOR_MATERIAL); - glEnable(GL_SCISSOR_TEST); - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); - - // [Bruno Levy] 05/18/2016 Added these missing state variables. - glDisable(GL_CLIP_PLANE0); - glActiveTexture(GL_TEXTURE0); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - glDisable(GL_LIGHTING); - glMatrixMode(GL_TEXTURE); - glPushMatrix(); - glLoadIdentity(); - - glEnable(GL_TEXTURE_2D); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - //glUseProgram(0); // You may want this if using this code in an OpenGL 3+ context where shaders may be bound - - // Setup viewport, orthographic projection matrix - // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayMin is typically (0,0) for single viewport apps. - glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height); - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glOrtho(draw_data->DisplayPos.x, draw_data->DisplayPos.x + draw_data->DisplaySize.x, draw_data->DisplayPos.y + draw_data->DisplaySize.y, draw_data->DisplayPos.y, -1.0f, +1.0f); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - - // Render command lists - ImVec2 pos = draw_data->DisplayPos; - for (int n = 0; n < draw_data->CmdListsCount; n++) - { - const ImDrawList* cmd_list = draw_data->CmdLists[n]; - const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; - const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; - glVertexPointer(2, GL_FLOAT, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, pos))); - glTexCoordPointer(2, GL_FLOAT, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, uv))); - glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, col))); - - for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) - { - const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; - if (pcmd->UserCallback) - { - // User callback (registered via ImDrawList::AddCallback) - pcmd->UserCallback(cmd_list, pcmd); - } - else - { - ImVec4 clip_rect = ImVec4(pcmd->ClipRect.x - pos.x, pcmd->ClipRect.y - pos.y, pcmd->ClipRect.z - pos.x, pcmd->ClipRect.w - pos.y); - if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f) - { - // Apply scissor/clipping rectangle - glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y)); - - // Bind texture, Draw - glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId); - glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer); - } - } - idx_buffer += pcmd->ElemCount; - } - } - - // Restore modified state - glDisableClientState(GL_COLOR_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - glDisableClientState(GL_VERTEX_ARRAY); - glBindTexture(GL_TEXTURE_2D, (GLuint)last_texture); - - glMatrixMode(GL_TEXTURE); // [Bruno Levy] 05/18/2016 Restore texture matrix. - glPopMatrix(); - - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glPopAttrib(); - glPolygonMode(GL_FRONT, (GLenum)last_polygon_mode[0]); glPolygonMode(GL_BACK, (GLenum)last_polygon_mode[1]); - glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]); - glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]); -} - -bool ImGui_ImplOpenGL2_CreateFontsTexture() -{ - // Build texture atlas - ImGuiIO& io = ImGui::GetIO(); - unsigned char* pixels; - int width, height; - io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bits (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory. - - // Upload texture to graphics system - GLint last_texture; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - glGenTextures(1, &g_FontTexture); - glBindTexture(GL_TEXTURE_2D, g_FontTexture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); - - // Store our identifier - io.Fonts->TexID = (ImTextureID)(intptr_t)g_FontTexture; - - // Restore state - glBindTexture(GL_TEXTURE_2D, last_texture); - - return true; -} - -void ImGui_ImplOpenGL2_DestroyFontsTexture() -{ - if (g_FontTexture) - { - ImGuiIO& io = ImGui::GetIO(); - glDeleteTextures(1, &g_FontTexture); - io.Fonts->TexID = 0; - g_FontTexture = 0; - } -} - -bool ImGui_ImplOpenGL2_CreateDeviceObjects() -{ - return ImGui_ImplOpenGL2_CreateFontsTexture(); -} - -void ImGui_ImplOpenGL2_DestroyDeviceObjects() -{ - ImGui_ImplOpenGL2_DestroyFontsTexture(); -} - -#endif // [Bruno Levy] conditional compile. diff --git a/src/lib/geogram_gfx/third_party/ImGui/imgui_impl_opengl2.h b/src/lib/geogram_gfx/third_party/ImGui/imgui_impl_opengl2.h deleted file mode 100644 index cb9e1d2b..00000000 --- a/src/lib/geogram_gfx/third_party/ImGui/imgui_impl_opengl2.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * dear imgui: Renderer for OpenGL2 (legacy OpenGL, fixed pipeline) - * This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..) - * - * Implemented features: - * [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void* / ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. - * - * You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. - * If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp. - * https: *github.com/ocornut/imgui - * - * **DO NOT USE THIS CODE IF YOUR CODE/ENGINE IS USING MODERN OPENGL (SHADERS, VBO, VAO, etc.)** - * **Prefer using the code in imgui_impl_opengl3.cpp** - * This code is mostly provided as a reference to learn how ImGui integration works, because it is shorter to read. - * If your code is using GL3+ context or any semi modern OpenGL calls, using this is likely to make everything more - * complicated, will require your code to reset every single OpenGL attributes to their initial state, and might - * confuse your GPU driver. - * The GL2 code is unable to reset attributes or even call e.g. "glUseProgram(0)" because they don't exist in that API. - */ - -/* [Bruno] C-style comment */ - -// [Bruno 05/16/2016] conditional compilation -#include -#ifdef GEO_GL_LEGACY - -/* [Bruno] */ -#ifdef __cplusplus -extern "C" { -#endif - -#pragma once - -IMGUI_IMPL_API bool ImGui_ImplOpenGL2_Init(); -IMGUI_IMPL_API void ImGui_ImplOpenGL2_Shutdown(); -IMGUI_IMPL_API void ImGui_ImplOpenGL2_NewFrame(); -IMGUI_IMPL_API void ImGui_ImplOpenGL2_RenderDrawData(ImDrawData* draw_data); - -/* Called by Init/NewFrame/Shutdown */ -IMGUI_IMPL_API bool ImGui_ImplOpenGL2_CreateFontsTexture(); -IMGUI_IMPL_API void ImGui_ImplOpenGL2_DestroyFontsTexture(); -IMGUI_IMPL_API bool ImGui_ImplOpenGL2_CreateDeviceObjects(); -IMGUI_IMPL_API void ImGui_ImplOpenGL2_DestroyDeviceObjects(); - -/* [Bruno] */ -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/src/lib/geogram_gfx/third_party/ImGui/imgui_impl_opengl3.cpp b/src/lib/geogram_gfx/third_party/ImGui/imgui_impl_opengl3.cpp index 7f519509..f2b9065d 100644 --- a/src/lib/geogram_gfx/third_party/ImGui/imgui_impl_opengl3.cpp +++ b/src/lib/geogram_gfx/third_party/ImGui/imgui_impl_opengl3.cpp @@ -1,6 +1,7 @@ -// dear imgui: Renderer for OpenGL3 / OpenGL ES2 / OpenGL ES3 (modern OpenGL with shaders / programmatic pipeline) +// dear imgui: Renderer for modern OpenGL with shaders / programmatic pipeline +// - Desktop GL: 3.x 4.x +// - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0) // This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..) -// (Note: We are using GL3W as a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc..) // Implemented features: // [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. @@ -11,7 +12,12 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2018-11-13: OpenGL: Support for GL 4.5's glClipControl(GL_UPPER_LEFT). +// 2019-03-03: OpenGL: Fix support for ES 2.0 (WebGL 1.0). +// 2019-02-20: OpenGL: Fix for OSX not supporting OpenGL 4.5, we don't try to read GL_CLIP_ORIGIN even if defined by the headers/loader. +// 2019-02-11: OpenGL: Projecting clipping rectangles correctly using draw_data->FramebufferScale to allow multi-viewports for retina display. +// 2019-02-01: OpenGL: Using GLSL 410 shaders for any version over 410 (e.g. 430, 450). +// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window. +// 2018-11-13: OpenGL: Support for GL 4.5's glClipControl(GL_UPPER_LEFT) / GL_CLIP_ORIGIN. // 2018-08-29: OpenGL: Added support for more OpenGL loaders: glew and glad, with comments indicative that any loader can be used. // 2018-08-09: OpenGL: Default to OpenGL ES 3 on iOS and Android. GLSL version default to "#version 300 ES". // 2018-07-30: OpenGL: Support for GLSL 300 ES and 410 core. Fixes for Emscripten compilation. @@ -35,17 +41,17 @@ // version version string //---------------------------------------- // 2.0 110 "#version 110" -// 2.1 120 -// 3.0 130 -// 3.1 140 +// 2.1 120 "#version 120" +// 3.0 130 "#version 130" +// 3.1 140 "#version 140" // 3.2 150 "#version 150" -// 3.3 330 -// 4.0 400 +// 3.3 330 "#version 330 core" +// 4.0 400 "#version 400 core" // 4.1 410 "#version 410 core" -// 4.2 420 -// 4.3 430 -// ES 2.0 100 "#version 100" -// ES 3.0 300 "#version 300 es" +// 4.2 420 "#version 410 core" +// 4.3 430 "#version 430 core" +// ES 2.0 100 "#version 100" = WebGL 1.0 +// ES 3.0 300 "#version 300 es" = WebGL 2.0 //---------------------------------------- #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) @@ -64,20 +70,24 @@ #include "TargetConditionals.h" #endif -// iOS, Android and Emscripten can use GL ES 3 -// Call ImGui_ImplOpenGL3_Init() with "#version 300 es" -#if (defined(__APPLE__) && TARGET_OS_IOS) || (defined(__ANDROID__)) || (defined(__EMSCRIPTEN__)) -#define USE_GL_ES3 +// Auto-detect GL version +#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) +#if (defined(__APPLE__) && TARGET_OS_IOS) || (defined(__ANDROID__)) +#define IMGUI_IMPL_OPENGL_ES3 // iOS, Android -> GL ES 3, "#version 300 es" +#elif defined(__EMSCRIPTEN__) +#define IMGUI_IMPL_OPENGL_ES2 // Emscripten -> GL ES 2, "#version 100" +#endif #endif -#if defined(USE_GL_ES3) && !defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM) // [Bruno] else glBindSampler() undefined with Emscripten -// OpenGL ES 3 +#if defined(IMGUI_IMPL_OPENGL_ES2) +#include +#elif defined(IMGUI_IMPL_OPENGL_ES3) && !defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM) // [Bruno Levy] else glBindSampler() undefined with Emscripten #include // Use GL ES 3 #else -// Regular OpenGL -// About OpenGL function loaders: modern OpenGL doesn't have a standard header file and requires individual function pointers to be loaded manually. -// Helper libraries are often used for this purpose! Here we are supporting a few common ones: gl3w, glew, glad. -// You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own. +// About Desktop OpenGL function loaders: +// Modern desktop OpenGL doesn't have a standard portable header file to load OpenGL function pointers. +// Helper libraries are often used for this purpose! Here we are supporting a few common ones (gl3w, glew, glad). +// You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own. #if defined(IMGUI_IMPL_OPENGL_LOADER_GL3W) #include #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLEW) @@ -100,8 +110,14 @@ static unsigned int g_VboHandle = 0, g_ElementsHandle = 0; // Functions bool ImGui_ImplOpenGL3_Init(const char* glsl_version) { + ImGuiIO& io = ImGui::GetIO(); + io.BackendRendererName = "imgui_impl_opengl3"; + // Store GLSL version string so we can refer to it later in case we recreate shaders. Note: GLSL version is NOT the same as GL version. Leave this to NULL if unsure. -#ifdef USE_GL_ES3 +#if defined(IMGUI_IMPL_OPENGL_ES2) + if (glsl_version == NULL) + glsl_version = "#version 100"; +#elif defined(IMGUI_IMPL_OPENGL_ES3) if (glsl_version == NULL) glsl_version = "#version 300 es"; #else @@ -111,6 +127,7 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(g_GlslVersionString)); strcpy(g_GlslVersionString, glsl_version); strcat(g_GlslVersionString, "\n"); + return true; } @@ -131,12 +148,10 @@ void ImGui_ImplOpenGL3_NewFrame() void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) { // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) - ImGuiIO& io = ImGui::GetIO(); - int fb_width = (int)(draw_data->DisplaySize.x * io.DisplayFramebufferScale.x); - int fb_height = (int)(draw_data->DisplaySize.y * io.DisplayFramebufferScale.y); + int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x); + int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y); if (fb_width <= 0 || fb_height <= 0) return; - draw_data->ScaleClipRects(io.DisplayFramebufferScale); // Backup GL state GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint*)&last_active_texture); @@ -147,7 +162,9 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) GLint last_sampler; glGetIntegerv(GL_SAMPLER_BINDING, &last_sampler); #endif GLint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); +#ifndef IMGUI_IMPL_OPENGL_ES2 GLint last_vertex_array; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array); +#endif #ifdef GL_POLYGON_MODE GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode); #endif @@ -164,7 +181,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST); GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST); bool clip_origin_lower_left = true; -#ifdef GL_CLIP_ORIGIN +#if defined(GL_CLIP_ORIGIN) && !defined(__APPLE__) GLenum last_clip_origin = 0; glGetIntegerv(GL_CLIP_ORIGIN, (GLint*)&last_clip_origin); // Support for GL 4.5's glClipControl(GL_UPPER_LEFT) if (last_clip_origin == GL_UPPER_LEFT) clip_origin_lower_left = false; @@ -201,11 +218,14 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) #ifdef GL_SAMPLER_BINDING glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 may set that otherwise. #endif + +#ifndef IMGUI_IMPL_OPENGL_ES2 // Recreate the VAO every time // (This is to easily allow multiple GL contexts. VAO are not shared among GL contexts, and we don't track creation/deletion of windows so we don't have an obvious key to use to cache them.) GLuint vao_handle = 0; glGenVertexArrays(1, &vao_handle); glBindVertexArray(vao_handle); +#endif glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle); glEnableVertexAttribArray(g_AttribLocationPosition); glEnableVertexAttribArray(g_AttribLocationUV); @@ -214,12 +234,15 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) glVertexAttribPointer(g_AttribLocationUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, uv)); glVertexAttribPointer(g_AttribLocationColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col)); - // Draw - ImVec2 pos = draw_data->DisplayPos; + // Will project scissor/clipping rectangles into framebuffer space + ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports + ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2) + + // Render command lists for (int n = 0; n < draw_data->CmdListsCount; n++) { const ImDrawList* cmd_list = draw_data->CmdLists[n]; - const ImDrawIdx* idx_buffer_offset = 0; + size_t idx_buffer_offset = 0; glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle); glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)cmd_list->VtxBuffer.Size * sizeof(ImDrawVert), (const GLvoid*)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW); @@ -237,7 +260,13 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) } else { - ImVec4 clip_rect = ImVec4(pcmd->ClipRect.x - pos.x, pcmd->ClipRect.y - pos.y, pcmd->ClipRect.z - pos.x, pcmd->ClipRect.w - pos.y); + // Project scissor/clipping rectangles into framebuffer space + ImVec4 clip_rect; + clip_rect.x = (pcmd->ClipRect.x - clip_off.x) * clip_scale.x; + clip_rect.y = (pcmd->ClipRect.y - clip_off.y) * clip_scale.y; + clip_rect.z = (pcmd->ClipRect.z - clip_off.x) * clip_scale.x; + clip_rect.w = (pcmd->ClipRect.w - clip_off.y) * clip_scale.y; + if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f) { // Apply scissor/clipping rectangle @@ -248,13 +277,15 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) // Bind texture, Draw glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId); - glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset); + glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)idx_buffer_offset); } } - idx_buffer_offset += pcmd->ElemCount; + idx_buffer_offset += pcmd->ElemCount * sizeof(ImDrawIdx); } } +#ifndef IMGUI_IMPL_OPENGL_ES2 glDeleteVertexArrays(1, &vao_handle); +#endif // Restore modified GL state glUseProgram(last_program); @@ -263,7 +294,9 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) glBindSampler(0, last_sampler); #endif glActiveTexture(last_active_texture); +#ifndef IMGUI_IMPL_OPENGL_ES2 glBindVertexArray(last_vertex_array); +#endif glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha); glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha); @@ -293,9 +326,9 @@ bool ImGui_ImplOpenGL3_CreateFontsTexture() glBindTexture(GL_TEXTURE_2D, g_FontTexture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); -#ifdef GL_UNPACK_ROW_LENGTH // [Bruno] +#ifdef GL_UNPACK_ROW_LENGTH glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); -#endif +#endif glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); // Store our identifier @@ -357,10 +390,13 @@ static bool CheckProgram(GLuint handle, const char* desc) bool ImGui_ImplOpenGL3_CreateDeviceObjects() { // Backup GL state - GLint last_texture, last_array_buffer, last_vertex_array; + GLint last_texture, last_array_buffer; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); +#ifndef IMGUI_IMPL_OPENGL_ES2 + GLint last_vertex_array; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array); +#endif // Parse GLSL version string int glsl_version = 130; @@ -474,7 +510,7 @@ bool ImGui_ImplOpenGL3_CreateDeviceObjects() vertex_shader = vertex_shader_glsl_120; fragment_shader = fragment_shader_glsl_120; } - else if (glsl_version == 410) + else if (glsl_version >= 410) { vertex_shader = vertex_shader_glsl_410_core; fragment_shader = fragment_shader_glsl_410_core; @@ -524,7 +560,9 @@ bool ImGui_ImplOpenGL3_CreateDeviceObjects() // Restore modified GL state glBindTexture(GL_TEXTURE_2D, last_texture); glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); +#ifndef IMGUI_IMPL_OPENGL_ES2 glBindVertexArray(last_vertex_array); +#endif return true; } diff --git a/src/lib/geogram_gfx/third_party/ImGui/imgui_impl_opengl3.h b/src/lib/geogram_gfx/third_party/ImGui/imgui_impl_opengl3.h index 1e44b545..ea17c19d 100644 --- a/src/lib/geogram_gfx/third_party/ImGui/imgui_impl_opengl3.h +++ b/src/lib/geogram_gfx/third_party/ImGui/imgui_impl_opengl3.h @@ -1,36 +1,43 @@ /* - * dear imgui: Renderer for OpenGL3 / OpenGL ES2 / OpenGL ES3 (modern OpenGL with shaders / programmatic pipeline) + * dear imgui: Renderer for modern OpenGL with shaders / programmatic pipeline + * - Desktop GL: 3.x 4.x + * - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0) * This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..) - * (Note: We are using GL3W as a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc..) - * + * Implemented features: * [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void* / ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. - * + * You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. * If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp. * https: *github.com/ocornut/imgui - * - * About OpenGL function loaders: - * About OpenGL function loaders: modern OpenGL doesn't have a standard header file and requires individual function pointers to be loaded manually. - * Helper libraries are often used for this purpose! Here we are supporting a few common ones: gl3w, glew, glad. - * You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own. - * + + * About Desktop OpenGL function loaders: + * Modern desktop OpenGL doesn't have a standard portable header file to load OpenGL function pointers. + * Helper libraries are often used for this purpose! Here we are supporting a few common ones (gl3w, glew, glad). + * You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own. + * About GLSL version: - * The 'glsl_version' initialization parameter should be NULL (default) or a "#version XXX" string. - * On computer platform the GLSL version default to "#version 130". On OpenGL ES 3 platform it defaults to "#version 300 es" - * Only override if your GL version doesn't handle this GLSL version. See GLSL version table at the top of imgui_impl_opengl3.cpp. + * The 'glsl_version' initialization parameter should be NULL (default) or a "#version XXX" string. + * On computer platform the GLSL version default to "#version 130". On OpenGL ES 3 platform it defaults to "#version 300 es" + * Only override if your GL version doesn't handle this GLSL version. See GLSL version table at the top of imgui_impl_opengl3.cpp. */ -/* [Bruno] C-style comment */ - +/* [Bruno Levy] C-style comment */ + #pragma once -/* [Bruno] */ +/* [Bruno Levy] */ #ifdef __cplusplus extern "C" { #endif -/* Set default OpenGL loader to be gl3w */ +/* + * Specific OpenGL versions + *#define IMGUI_IMPL_OPENGL_ES2 // Auto-detected on Emscripten + *#define IMGUI_IMPL_OPENGL_ES3 // Auto-detected on iOS/Android + */ + +/* Set default OpenGL3 loader to be gl3w */ #if !defined(IMGUI_IMPL_OPENGL_LOADER_GL3W) \ && !defined(IMGUI_IMPL_OPENGL_LOADER_GLEW) \ && !defined(IMGUI_IMPL_OPENGL_LOADER_GLAD) \ @@ -49,7 +56,7 @@ IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyFontsTexture(); IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateDeviceObjects(); IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects(); -/* [Bruno] */ +/* [Bruno Levy] */ #ifdef __cplusplus } #endif diff --git a/src/lib/geogram_gfx/third_party/ImGui/imgui_impl_win32.cpp b/src/lib/geogram_gfx/third_party/ImGui/imgui_impl_win32.cpp index 4a6276c5..335d7ed7 100644 --- a/src/lib/geogram_gfx/third_party/ImGui/imgui_impl_win32.cpp +++ b/src/lib/geogram_gfx/third_party/ImGui/imgui_impl_win32.cpp @@ -5,10 +5,9 @@ // [X] Platform: Clipboard support (for Win32 this is actually part of core imgui) // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. // [X] Platform: Keyboard arrays indexed using VK_* Virtual Key Codes, e.g. ImGui::IsKeyPressed(VK_SPACE). -// Missing features: -// [ ] Platform: Gamepad support (best leaving it to user application to fill io.NavInputs[] with gamepad inputs from their source of choice). +// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. -#ifdef _WIN32 /* [Bruno] */ +#ifdef _WIN32 /* [Bruno Levy] */ #include "imgui.h" #include "imgui_impl_win32.h" @@ -18,8 +17,17 @@ #include #include +// [Bruno Levy] +#ifdef WITH_IMGUI_GAMEPAD +#include +#endif + // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2019-01-17: Misc: Using GetForegroundWindow()+IsChild() instead of GetActiveWindow() to be compatible with windows created in a different thread or parent. +// 2019-01-17: Inputs: Added support for mouse buttons 4 and 5 via WM_XBUTTON* messages. +// 2019-01-15: Inputs: Added support for XInput gamepads (if ImGuiConfigFlags_NavEnableGamepad is set by user application). +// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window. // 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor. // 2018-06-10: Inputs: Fixed handling of mouse wheel messages to support fine position messages (typically sent by track-pads). // 2018-06-08: Misc: Extracted imgui_impl_win32.cpp/.h away from the old combined DX9/DX10/DX11/DX12 examples. @@ -32,7 +40,7 @@ // 2018-01-08: Inputs: Added mapping for ImGuiKey_Insert. // 2018-01-05: Inputs: Added WM_LBUTTONDBLCLK double-click handlers for window classes with the CS_DBLCLKS flag. // 2017-10-23: Inputs: Added WM_SYSKEYDOWN / WM_SYSKEYUP handlers so e.g. the VK_MENU key can be read. -// 2017-10-23: Inputs: Using Win32 ::SetCapture/::GetCapture() to retrieve mouse positions outside the client area when dragging. +// 2017-10-23: Inputs: Using Win32 ::SetCapture/::GetCapture() to retrieve mouse positions outside the client area when dragging. // 2016-11-12: Inputs: Only call Win32 ::SetCursor(NULL) when io.MouseDrawCursor is set. // Win32 Data @@ -40,6 +48,8 @@ static HWND g_hWnd = 0; static INT64 g_Time = 0; static INT64 g_TicksPerSecond = 0; static ImGuiMouseCursor g_LastMouseCursor = ImGuiMouseCursor_COUNT; +static bool g_HasGamepad = false; +static bool g_WantUpdateHasGamepad = true; // Functions bool ImGui_ImplWin32_Init(void* hwnd) @@ -54,6 +64,7 @@ bool ImGui_ImplWin32_Init(void* hwnd) ImGuiIO& io = ImGui::GetIO(); io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) + io.BackendPlatformName = "imgui_impl_win32"; io.ImeWindowHandle = hwnd; // Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array that we will update during the application lifetime. @@ -134,14 +145,74 @@ static void ImGui_ImplWin32_UpdateMousePos() // Set mouse position io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); POINT pos; - if (::GetActiveWindow() == g_hWnd && ::GetCursorPos(&pos)) - if (::ScreenToClient(g_hWnd, &pos)) - io.MousePos = ImVec2((float)pos.x, (float)pos.y); + if (HWND active_window = ::GetForegroundWindow()) + if (active_window == g_hWnd || ::IsChild(active_window, g_hWnd)) + if (::GetCursorPos(&pos) && ::ScreenToClient(g_hWnd, &pos)) + io.MousePos = ImVec2((float)pos.x, (float)pos.y); +} + +// [Bruno Levy] +#ifdef WITH_IMGUI_GAMEPAD + +#ifdef _MSC_VER +#pragma comment(lib, "xinput") +#endif + +// Gamepad navigation mapping +static void ImGui_ImplWin32_UpdateGamepads() +{ + ImGuiIO& io = ImGui::GetIO(); + memset(io.NavInputs, 0, sizeof(io.NavInputs)); + if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) + return; + + // Calling XInputGetState() every frame on disconnected gamepads is unfortunately too slow. + // Instead we refresh gamepad availability by calling XInputGetCapabilities() _only_ after receiving WM_DEVICECHANGE. + if (g_WantUpdateHasGamepad) + { + XINPUT_CAPABILITIES caps; + g_HasGamepad = (XInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS); + g_WantUpdateHasGamepad = false; + } + + XINPUT_STATE xinput_state; + io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; + if (g_HasGamepad && XInputGetState(0, &xinput_state) == ERROR_SUCCESS) + { + const XINPUT_GAMEPAD& gamepad = xinput_state.Gamepad; + io.BackendFlags |= ImGuiBackendFlags_HasGamepad; + + #define MAP_BUTTON(NAV_NO, BUTTON_ENUM) { io.NavInputs[NAV_NO] = (gamepad.wButtons & BUTTON_ENUM) ? 1.0f : 0.0f; } + #define MAP_ANALOG(NAV_NO, VALUE, V0, V1) { float vn = (float)(VALUE - V0) / (float)(V1 - V0); if (vn > 1.0f) vn = 1.0f; if (vn > 0.0f && io.NavInputs[NAV_NO] < vn) io.NavInputs[NAV_NO] = vn; } + MAP_BUTTON(ImGuiNavInput_Activate, XINPUT_GAMEPAD_A); // Cross / A + MAP_BUTTON(ImGuiNavInput_Cancel, XINPUT_GAMEPAD_B); // Circle / B + MAP_BUTTON(ImGuiNavInput_Menu, XINPUT_GAMEPAD_X); // Square / X + MAP_BUTTON(ImGuiNavInput_Input, XINPUT_GAMEPAD_Y); // Triangle / Y + MAP_BUTTON(ImGuiNavInput_DpadLeft, XINPUT_GAMEPAD_DPAD_LEFT); // D-Pad Left + MAP_BUTTON(ImGuiNavInput_DpadRight, XINPUT_GAMEPAD_DPAD_RIGHT); // D-Pad Right + MAP_BUTTON(ImGuiNavInput_DpadUp, XINPUT_GAMEPAD_DPAD_UP); // D-Pad Up + MAP_BUTTON(ImGuiNavInput_DpadDown, XINPUT_GAMEPAD_DPAD_DOWN); // D-Pad Down + MAP_BUTTON(ImGuiNavInput_FocusPrev, XINPUT_GAMEPAD_LEFT_SHOULDER); // L1 / LB + MAP_BUTTON(ImGuiNavInput_FocusNext, XINPUT_GAMEPAD_RIGHT_SHOULDER); // R1 / RB + MAP_BUTTON(ImGuiNavInput_TweakSlow, XINPUT_GAMEPAD_LEFT_SHOULDER); // L1 / LB + MAP_BUTTON(ImGuiNavInput_TweakFast, XINPUT_GAMEPAD_RIGHT_SHOULDER); // R1 / RB + MAP_ANALOG(ImGuiNavInput_LStickLeft, gamepad.sThumbLX, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768); + MAP_ANALOG(ImGuiNavInput_LStickRight, gamepad.sThumbLX, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767); + MAP_ANALOG(ImGuiNavInput_LStickUp, gamepad.sThumbLY, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767); + MAP_ANALOG(ImGuiNavInput_LStickDown, gamepad.sThumbLY, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32767); + #undef MAP_BUTTON + #undef MAP_ANALOG + } } +// [Bruno Levy] +#endif + + void ImGui_ImplWin32_NewFrame() { ImGuiIO& io = ImGui::GetIO(); + IM_ASSERT(io.Fonts->IsBuilt() && "Font atlas not built! It is generally built by the renderer back-end. Missing call to renderer _NewFrame() function? e.g. ImGui_ImplOpenGL3_NewFrame()."); // Setup display size (every frame to accommodate for window resizing) RECT rect; @@ -171,19 +242,27 @@ void ImGui_ImplWin32_NewFrame() g_LastMouseCursor = mouse_cursor; ImGui_ImplWin32_UpdateMouseCursor(); } + +#ifdef WITH_IMGUI_GAMEPAD // [Bruno Levy] + // Update game controllers (if available) + ImGui_ImplWin32_UpdateGamepads(); +#endif } // Allow compilation with old Windows SDK. MinGW doesn't have default _WIN32_WINNT/WINVER versions. #ifndef WM_MOUSEHWHEEL #define WM_MOUSEHWHEEL 0x020E #endif +#ifndef DBT_DEVNODES_CHANGED +#define DBT_DEVNODES_CHANGED 0x0007 +#endif -// Process Win32 mouse/keyboard inputs. +// Process Win32 mouse/keyboard inputs. // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. -// PS: In this Win32 handler, we use the capture API (GetCapture/SetCapture/ReleaseCapture) to be able to read mouse coordinations when dragging mouse outside of our window bounds. +// PS: In this Win32 handler, we use the capture API (GetCapture/SetCapture/ReleaseCapture) to be able to read mouse coordinates when dragging mouse outside of our window bounds. // PS: We treat DBLCLK messages as regular mouse down messages, so this code will work on windows classes that have the CS_DBLCLKS flag set. Our own example app code doesn't set this flag. IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { @@ -196,11 +275,13 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK: case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK: + case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK: { int button = 0; - if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONDBLCLK) button = 0; - if (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONDBLCLK) button = 1; - if (msg == WM_MBUTTONDOWN || msg == WM_MBUTTONDBLCLK) button = 2; + if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONDBLCLK) { button = 0; } + if (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONDBLCLK) { button = 1; } + if (msg == WM_MBUTTONDOWN || msg == WM_MBUTTONDBLCLK) { button = 2; } + if (msg == WM_XBUTTONDOWN || msg == WM_XBUTTONDBLCLK) { button = (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) ? 3 : 4; } if (!ImGui::IsAnyMouseDown() && ::GetCapture() == NULL) ::SetCapture(hwnd); io.MouseDown[button] = true; @@ -209,11 +290,13 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA case WM_LBUTTONUP: case WM_RBUTTONUP: case WM_MBUTTONUP: + case WM_XBUTTONUP: { int button = 0; - if (msg == WM_LBUTTONUP) button = 0; - if (msg == WM_RBUTTONUP) button = 1; - if (msg == WM_MBUTTONUP) button = 2; + if (msg == WM_LBUTTONUP) { button = 0; } + if (msg == WM_RBUTTONUP) { button = 1; } + if (msg == WM_MBUTTONUP) { button = 2; } + if (msg == WM_XBUTTONUP) { button = (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) ? 3 : 4; } io.MouseDown[button] = false; if (!ImGui::IsAnyMouseDown() && ::GetCapture() == hwnd) ::ReleaseCapture(); @@ -244,9 +327,12 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA if (LOWORD(lParam) == HTCLIENT && ImGui_ImplWin32_UpdateMouseCursor()) return 1; return 0; + case WM_DEVICECHANGE: + if ((UINT)wParam == DBT_DEVNODES_CHANGED) + g_WantUpdateHasGamepad = true; + return 0; } return 0; } -#endif /* [Bruno] */ - +#endif /* [Bruno Levy] */ diff --git a/src/lib/geogram_gfx/third_party/ImGui/imgui_impl_win32.h b/src/lib/geogram_gfx/third_party/ImGui/imgui_impl_win32.h index 7f687ed8..b3ab5742 100644 --- a/src/lib/geogram_gfx/third_party/ImGui/imgui_impl_win32.h +++ b/src/lib/geogram_gfx/third_party/ImGui/imgui_impl_win32.h @@ -1,26 +1,24 @@ /* * dear imgui: Platform Binding for Windows (standard windows API for 32 and 64 bits applications) * This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..) - * + * Implemented features: * [X] Platform: Clipboard support (for Win32 this is actually part of core imgui) * [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. * [X] Platform: Keyboard arrays indexed using VK_* Virtual Key Codes, e.g. ImGui::IsKeyPressed(VK_SPACE). - * Missing features: - * [ ] Platform: Gamepad support (best leaving it to user application to fill io.NavInputs[] with gamepad inputs from their source of choice). + * [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. */ -/* [Bruno] C-style comment */ +/* [Bruno Levy] C-style comment */ #pragma once -/* [Bruno] */ +/* [Bruno Levy] */ #ifdef _WIN32 #ifdef __cplusplus extern "C" { #endif - IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd); IMGUI_IMPL_API void ImGui_ImplWin32_Shutdown(); IMGUI_IMPL_API void ImGui_ImplWin32_NewFrame(); @@ -28,9 +26,8 @@ IMGUI_IMPL_API void ImGui_ImplWin32_NewFrame(); /* * Handler for Win32 messages, update mouse/keyboard data. * You may or not need this for your implementation, but it can serve as reference for handling inputs. - * Intentionally commented out to avoid dragging dependencies on types. You can copy the extern declaration in your code. + * Intentionally commented out to avoid dragging dependencies on types. You can COPY this line into your .cpp code instead. */ - /* IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); */ @@ -41,3 +38,4 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPAR #endif #endif + diff --git a/src/lib/geogram_gfx/third_party/ImGui/imgui_internal.h b/src/lib/geogram_gfx/third_party/ImGui/imgui_internal.h index ca90975f..f1615e7d 100644 --- a/src/lib/geogram_gfx/third_party/ImGui/imgui_internal.h +++ b/src/lib/geogram_gfx/third_party/ImGui/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.66 +// dear imgui, v1.69 // (internal structures/api) // You may use this file to debug, understand or extend ImGui features but we don't provide any guarantee of forward compatibility! @@ -6,8 +6,27 @@ // #define IMGUI_DEFINE_MATH_OPERATORS // To implement maths operators for ImVec2 (disabled by default to not collide with using IM_VEC2_CLASS_EXTRA along with your own math types+operators) +/* + +Index of this file: +// Header mess +// Forward declarations +// STB libraries includes +// Context pointer +// Generic helpers +// Misc data structures +// Main imgui context +// Tab bar, tab item +// Internal API + +*/ + #pragma once +//----------------------------------------------------------------------------- +// Header mess +//----------------------------------------------------------------------------- + #ifndef IMGUI_VERSION #error Must include imgui.h before imgui_internal.h #endif @@ -24,13 +43,19 @@ #ifdef __clang__ #pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-function" // for stb_textedit.h -#pragma clang diagnostic ignored "-Wmissing-prototypes" // for stb_textedit.h +#pragma clang diagnostic ignored "-Wunused-function" // for stb_textedit.h +#pragma clang diagnostic ignored "-Wmissing-prototypes" // for stb_textedit.h #pragma clang diagnostic ignored "-Wold-style-cast" +#if __has_warning("-Wzero-as-null-pointer-constant") +#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" +#endif +#if __has_warning("-Wdouble-promotion") +#pragma clang diagnostic ignored "-Wdouble-promotion" +#endif #endif //----------------------------------------------------------------------------- -// Forward Declarations +// Forward declarations //----------------------------------------------------------------------------- struct ImRect; // An axis-aligned rectangle (2 points) @@ -49,6 +74,8 @@ struct ImGuiNextWindowData; // Storage for SetNexWindow** functions struct ImGuiPopupRef; // Storage for current popup stack struct ImGuiSettingsHandler; // Storage for one type registered in the .ini file struct ImGuiStyleMod; // Stacked style modifier, backup of modified data so we can restore it +struct ImGuiTabBar; // Storage for a tab bar +struct ImGuiTabItem; // Storage for a tab item (within a tab bar) struct ImGuiWindow; // Storage for one window struct ImGuiWindowTempData; // Temporary storage for one window (that's the data which in theory we could ditch at the end of the frame) struct ImGuiWindowSettings; // Storage for window settings stored in .ini file (we keep one of those even if the actual window wasn't instanced during this session) @@ -56,6 +83,7 @@ struct ImGuiWindowSettings; // Storage for window settings stored in .in // Use your programming IDE "Go to definition" facility on the names of the center columns to find the actual flags/enum lists. typedef int ImGuiLayoutType; // -> enum ImGuiLayoutType_ // Enum: Horizontal or vertical typedef int ImGuiButtonFlags; // -> enum ImGuiButtonFlags_ // Flags: for ButtonEx(), ButtonBehavior() +typedef int ImGuiDragFlags; // -> enum ImGuiDragFlags_ // Flags: for DragBehavior() typedef int ImGuiItemFlags; // -> enum ImGuiItemFlags_ // Flags: for PushItemFlag() typedef int ImGuiItemStatusFlags; // -> enum ImGuiItemStatusFlags_ // Flags: for DC.LastItemStatusFlags typedef int ImGuiNavHighlightFlags; // -> enum ImGuiNavHighlightFlags_ // Flags: for RenderNavHighlight() @@ -63,13 +91,13 @@ typedef int ImGuiNavDirSourceFlags; // -> enum ImGuiNavDirSourceFlags_ // Flags: typedef int ImGuiNavMoveFlags; // -> enum ImGuiNavMoveFlags_ // Flags: for navigation requests typedef int ImGuiSeparatorFlags; // -> enum ImGuiSeparatorFlags_ // Flags: for Separator() - internal typedef int ImGuiSliderFlags; // -> enum ImGuiSliderFlags_ // Flags: for SliderBehavior() -typedef int ImGuiDragFlags; // -> enum ImGuiDragFlags_ // Flags: for DragBehavior() +typedef int ImGuiTextFlags; // -> enum ImGuiTextFlags_ // Flags: for TextEx() //------------------------------------------------------------------------- -// STB libraries +// STB libraries includes //------------------------------------------------------------------------- -namespace ImGuiStb +namespace ImStb { #undef STB_TEXTEDIT_STRING @@ -77,12 +105,14 @@ namespace ImGuiStb #define STB_TEXTEDIT_STRING ImGuiInputTextState #define STB_TEXTEDIT_CHARTYPE ImWchar #define STB_TEXTEDIT_GETWIDTH_NEWLINE -1.0f +#define STB_TEXTEDIT_UNDOSTATECOUNT 99 +#define STB_TEXTEDIT_UNDOCHARCOUNT 999 #include "imstb_textedit.h" -} // namespace ImGuiStb +} // namespace ImStb //----------------------------------------------------------------------------- -// Context +// Context pointer //----------------------------------------------------------------------------- #ifndef GImGui @@ -90,7 +120,7 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit ImGui context pointe #endif //----------------------------------------------------------------------------- -// Helpers +// Generic helpers //----------------------------------------------------------------------------- #define IM_PI 3.14159265358979323846f @@ -99,6 +129,8 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit ImGui context pointe #else #define IM_NEWLINE "\n" #endif + +#define IMGUI_DEBUG_LOG(_FMT,...) printf("[%05d] " _FMT, GImGui->FrameCount, __VA_ARGS__) #define IM_STATIC_ASSERT(_COND) typedef char static_assertion_##__line__[(_COND)?1:-1] #define IM_F32_TO_INT8_UNBOUND(_VAL) ((int)((_VAL) * 255.0f + ((_VAL)>=0 ? 0.5f : -0.5f))) // Unsaturated, for display purpose #define IM_F32_TO_INT8_SAT(_VAL) ((int)(ImSaturate(_VAL) * 255.0f + 0.5f)) // Saturated, always output 0..255 @@ -119,7 +151,8 @@ IMGUI_API int ImTextCountUtf8BytesFromChar(const char* in_text, const IMGUI_API int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end); // return number of bytes to express string in UTF-8 // Helpers: Misc -IMGUI_API ImU32 ImHash(const void* data, int data_size, ImU32 seed = 0); // Pass data_size==0 for zero-terminated strings +IMGUI_API ImU32 ImHashData(const void* data, size_t data_size, ImU32 seed = 0); +IMGUI_API ImU32 ImHashStr(const char* data, size_t data_size, ImU32 seed = 0); IMGUI_API void* ImFileLoadToMemory(const char* filename, const char* file_open_mode, size_t* out_file_size = NULL, int padding_bytes = 0); IMGUI_API FILE* ImFileOpen(const char* filename, const char* file_open_mode); static inline bool ImCharIsBlankA(char c) { return c == ' ' || c == '\t'; } @@ -127,6 +160,9 @@ static inline bool ImCharIsBlankW(unsigned int c) { return c == ' ' || c = static inline bool ImIsPowerOfTwo(int v) { return v != 0 && (v & (v - 1)) == 0; } static inline int ImUpperPowerOfTwo(int v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; } #define ImQsort qsort +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS +static inline ImU32 ImHash(const void* data, int size, ImU32 seed = 0) { return size ? ImHashData(data, (size_t)size, seed) : ImHashStr((const char*)data, 0, seed); } // [moved to ImHashStr/ImHashData in 1.68] +#endif // Helpers: Geometry IMGUI_API ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p); @@ -140,6 +176,7 @@ IMGUI_API int ImStricmp(const char* str1, const char* str2); IMGUI_API int ImStrnicmp(const char* str1, const char* str2, size_t count); IMGUI_API void ImStrncpy(char* dst, const char* src, size_t count); IMGUI_API char* ImStrdup(const char* str); +IMGUI_API char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* str); IMGUI_API const char* ImStrchrRange(const char* str_begin, const char* str_end, char c); IMGUI_API int ImStrlenW(const ImWchar* str); IMGUI_API const char* ImStreolRange(const char* str, const char* str_end); // End end-of-line @@ -150,7 +187,7 @@ IMGUI_API int ImFormatString(char* buf, size_t buf_size, const char* f IMGUI_API int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args) IM_FMTLIST(3); IMGUI_API const char* ImParseFormatFindStart(const char* format); IMGUI_API const char* ImParseFormatFindEnd(const char* format); -IMGUI_API const char* ImParseFormatTrimDecorations(const char* format, char* buf, int buf_size); +IMGUI_API const char* ImParseFormatTrimDecorations(const char* format, char* buf, size_t buf_size); IMGUI_API int ImParseFormatPrecision(const char* format, int default_value); // Helpers: ImVec2/ImVec4 operators @@ -189,12 +226,15 @@ static inline double ImAtof(const char* s) static inline float ImFloorStd(float x) { return floorf(x); } // we already uses our own ImFloor() { return (float)(int)v } internally so the standard one wrapper is named differently (it's used by stb_truetype) static inline float ImCeil(float x) { return ceilf(x); } #endif -// - ImMin/ImMax/ImClamp/ImLerp/ImSwap are used by widgets which support for variety of types: signed/unsigned int/long long float/double, using templates here but we could also redefine them 6 times +// - ImMin/ImMax/ImClamp/ImLerp/ImSwap are used by widgets which support for variety of types: signed/unsigned int/long long float/double +// (Exceptionally using templates here but we could also redefine them for variety of types) template static inline T ImMin(T lhs, T rhs) { return lhs < rhs ? lhs : rhs; } template static inline T ImMax(T lhs, T rhs) { return lhs >= rhs ? lhs : rhs; } template static inline T ImClamp(T v, T mn, T mx) { return (v < mn) ? mn : (v > mx) ? mx : v; } template static inline T ImLerp(T a, T b, float t) { return (T)(a + (b - a) * t); } template static inline void ImSwap(T& a, T& b) { T tmp = a; a = b; b = tmp; } +template static inline T ImAddClampOverflow(T a, T b, T mn, T mx) { if (b < 0 && (a < mn - b)) return mn; if (b > 0 && (a > mx - b)) return mx; return a + b; } +template static inline T ImSubClampOverflow(T a, T b, T mn, T mx) { if (b > 0 && (a < mn + b)) return mn; if (b < 0 && (a > mx + b)) return mx; return a - b; } // - Misc maths helpers static inline ImVec2 ImMin(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x < rhs.x ? lhs.x : rhs.x, lhs.y < rhs.y ? lhs.y : rhs.y); } static inline ImVec2 ImMax(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x >= rhs.x ? lhs.x : rhs.x, lhs.y >= rhs.y ? lhs.y : rhs.y); } @@ -208,23 +248,53 @@ static inline float ImLengthSqr(const ImVec4& lhs) static inline float ImInvLength(const ImVec2& lhs, float fail_value) { float d = lhs.x*lhs.x + lhs.y*lhs.y; if (d > 0.0f) return 1.0f / ImSqrt(d); return fail_value; } static inline float ImFloor(float f) { return (float)(int)f; } static inline ImVec2 ImFloor(const ImVec2& v) { return ImVec2((float)(int)v.x, (float)(int)v.y); } +static inline int ImModPositive(int a, int b) { return (a + b) % b; } static inline float ImDot(const ImVec2& a, const ImVec2& b) { return a.x * b.x + a.y * b.y; } static inline ImVec2 ImRotate(const ImVec2& v, float cos_a, float sin_a) { return ImVec2(v.x * cos_a - v.y * sin_a, v.x * sin_a + v.y * cos_a); } static inline float ImLinearSweep(float current, float target, float speed) { if (current < target) return ImMin(current + speed, target); if (current > target) return ImMax(current - speed, target); return current; } static inline ImVec2 ImMul(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x * rhs.x, lhs.y * rhs.y); } -//----------------------------------------------------------------------------- -// Types -//----------------------------------------------------------------------------- +// Helper: ImBoolVector. Store 1-bit per value. +// Note that Resize() currently clears the whole vector. +struct ImBoolVector +{ + ImVector Storage; + ImBoolVector() { } + void Resize(int sz) { Storage.resize((sz + 31) >> 5); memset(Storage.Data, 0, (size_t)Storage.Size * sizeof(Storage.Data[0])); } + void Clear() { Storage.clear(); } + bool GetBit(int n) const { int off = (n >> 5); int mask = 1 << (n & 31); return (Storage[off] & mask) != 0; } + void SetBit(int n, bool v) { int off = (n >> 5); int mask = 1 << (n & 31); if (v) Storage[off] |= mask; else Storage[off] &= ~mask; } +}; -// 1D vector (this odd construct is used to facilitate the transition between 1D and 2D and maintenance of some patches) -struct ImVec1 +// Helper: ImPool<>. Basic keyed storage for contiguous instances, slow/amortized insertion, O(1) indexable, O(Log N) queries by ID over a dense/hot buffer, +// Honor constructor/destructor. Add/remove invalidate all pointers. Indexes have the same lifetime as the associated object. +typedef int ImPoolIdx; +template +struct IMGUI_API ImPool { - float x; - ImVec1() { x = 0.0f; } - ImVec1(float _x) { x = _x; } + ImVector Data; // Contiguous data + ImGuiStorage Map; // ID->Index + ImPoolIdx FreeIdx; // Next free idx to use + + ImPool() { FreeIdx = 0; } + ~ImPool() { Clear(); } + T* GetByKey(ImGuiID key) { int idx = Map.GetInt(key, -1); return (idx != -1) ? &Data[idx] : NULL; } + T* GetByIndex(ImPoolIdx n) { return &Data[n]; } + ImPoolIdx GetIndex(const T* p) const { IM_ASSERT(p >= Data.Data && p < Data.Data + Data.Size); return (ImPoolIdx)(p - Data.Data); } + T* GetOrAddByKey(ImGuiID key) { int* p_idx = Map.GetIntRef(key, -1); if (*p_idx != -1) return &Data[*p_idx]; *p_idx = FreeIdx; return Add(); } + bool Contains(const T* p) const { return (p >= Data.Data && p < Data.Data + Data.Size); } + void Clear() { for (int n = 0; n < Map.Data.Size; n++) { int idx = Map.Data[n].val_i; if (idx != -1) Data[idx].~T(); } Map.Clear(); Data.clear(); FreeIdx = 0; } + T* Add() { int idx = FreeIdx; if (idx == Data.Size) { Data.resize(Data.Size + 1); FreeIdx++; } else { FreeIdx = *(int*)&Data[idx]; } IM_PLACEMENT_NEW(&Data[idx]) T(); return &Data[idx]; } + void Remove(ImGuiID key, const T* p) { Remove(key, GetIndex(p)); } + void Remove(ImGuiID key, ImPoolIdx idx) { Data[idx].~T(); *(int*)&Data[idx] = FreeIdx; FreeIdx = idx; Map.SetInt(key, -1); } + void Reserve(int capacity) { Data.reserve(capacity); Map.Data.reserve(capacity); } + int GetSize() const { return Data.Size; } }; +//----------------------------------------------------------------------------- +// Misc data structures +//----------------------------------------------------------------------------- + enum ImGuiButtonFlags_ { ImGuiButtonFlags_None = 0, @@ -283,6 +353,19 @@ enum ImGuiSeparatorFlags_ ImGuiSeparatorFlags_Vertical = 1 << 1 }; +// Transient per-window flags, reset at the beginning of the frame. For child window, inherited from parent on first Begin(). +// This is going to be exposed in imgui.h when stabilized enough. +enum ImGuiItemFlags_ +{ + ImGuiItemFlags_NoTabStop = 1 << 0, // false + ImGuiItemFlags_ButtonRepeat = 1 << 1, // false // Button() will return true multiple times based on io.KeyRepeatDelay and io.KeyRepeatRate settings. + ImGuiItemFlags_Disabled = 1 << 2, // false // [BETA] Disable interactions but doesn't affect visuals yet. See github.com/ocornut/imgui/issues/211 + ImGuiItemFlags_NoNav = 1 << 3, // false + ImGuiItemFlags_NoNavDefaultFocus = 1 << 4, // false + ImGuiItemFlags_SelectableDontClosePopup = 1 << 5, // false // MenuItem/Selectable() automatically closes current Popup window + ImGuiItemFlags_Default_ = 0 +}; + // Storage for LastItem data enum ImGuiItemStatusFlags_ { @@ -290,15 +373,40 @@ enum ImGuiItemStatusFlags_ ImGuiItemStatusFlags_HoveredRect = 1 << 0, ImGuiItemStatusFlags_HasDisplayRect = 1 << 1, ImGuiItemStatusFlags_Edited = 1 << 2 // Value exposed by item was edited in the current frame (should match the bool return value of most widgets) + +#ifdef IMGUI_ENABLE_TEST_ENGINE + , // [imgui-test only] + ImGuiItemStatusFlags_Openable = 1 << 10, // + ImGuiItemStatusFlags_Opened = 1 << 11, // + ImGuiItemStatusFlags_Checkable = 1 << 12, // + ImGuiItemStatusFlags_Checked = 1 << 13 // +#endif +}; + +enum ImGuiTextFlags_ +{ + ImGuiTextFlags_None = 0, + ImGuiTextFlags_NoWidthForLargeClippedText = 1 << 0 }; // FIXME: this is in development, not exposed/functional as a generic feature yet. +// Horizontal/Vertical enums are fixed to 0/1 so they may be used to index ImVec2 enum ImGuiLayoutType_ { - ImGuiLayoutType_Vertical, - ImGuiLayoutType_Horizontal + ImGuiLayoutType_Horizontal = 0, + ImGuiLayoutType_Vertical = 1 +}; + +enum ImGuiLogType +{ + ImGuiLogType_None = 0, + ImGuiLogType_TTY, + ImGuiLogType_File, + ImGuiLogType_Buffer, + ImGuiLogType_Clipboard }; +// X/Y enums are fixed to 0/1 so they may be used to index ImVec2 enum ImGuiAxis { ImGuiAxis_None = -1, @@ -338,7 +446,7 @@ enum ImGuiNavHighlightFlags_ ImGuiNavHighlightFlags_None = 0, ImGuiNavHighlightFlags_TypeDefault = 1 << 0, ImGuiNavHighlightFlags_TypeThin = 1 << 1, - ImGuiNavHighlightFlags_AlwaysDraw = 1 << 2, + ImGuiNavHighlightFlags_AlwaysDraw = 1 << 2, // Draw rectangular highlight if (g.NavId == id) _even_ when using the mouse. ImGuiNavHighlightFlags_NoRounding = 1 << 3 }; @@ -368,12 +476,28 @@ enum ImGuiNavForward ImGuiNavForward_ForwardActive }; +enum ImGuiNavLayer +{ + ImGuiNavLayer_Main = 0, // Main scrolling layer + ImGuiNavLayer_Menu = 1, // Menu layer (access with Alt/ImGuiNavInput_Menu) + ImGuiNavLayer_COUNT +}; + enum ImGuiPopupPositionPolicy { ImGuiPopupPositionPolicy_Default, ImGuiPopupPositionPolicy_ComboBox }; +// 1D vector (this odd construct is used to facilitate the transition between 1D and 2D, and the maintenance of some branches/patches) +struct ImVec1 +{ + float x; + ImVec1() { x = 0.0f; } + ImVec1(float _x) { x = _x; } +}; + + // 2D axis aligned bounding-box // NB: we can't rely on ImVec2 math operators being available here struct IMGUI_API ImRect @@ -436,7 +560,6 @@ struct ImGuiGroupData ImVec1 BackupGroupOffset; ImVec2 BackupCurrentLineSize; float BackupCurrentLineTextBaseOffset; - float BackupLogLinePosY; ImGuiID BackupActiveIdIsAlive; bool BackupActiveIdPreviousFrameIsAlive; bool AdvanceCursor; @@ -445,10 +568,9 @@ struct ImGuiGroupData // Simple column measurement, currently used for MenuItem() only.. This is very short-sighted/throw-away code and NOT a generic helper. struct IMGUI_API ImGuiMenuColumns { - int Count; float Spacing; float Width, NextWidth; - float Pos[4], NextWidths[4]; + float Pos[3], NextWidths[3]; ImGuiMenuColumns(); void Update(int count, float spacing, bool clear); @@ -460,16 +582,17 @@ struct IMGUI_API ImGuiMenuColumns struct IMGUI_API ImGuiInputTextState { ImGuiID ID; // widget id owning the text state + int CurLenW, CurLenA; // we need to maintain our buffer length in both UTF-8 and wchar format. UTF-8 len is valid even if TextA is not. ImVector TextW; // edit buffer, we need to persist but can't guarantee the persistence of the user-provided buffer. so we copy into own buffer. - ImVector InitialText; // backup of end-user buffer at the time of focus (in UTF-8, unaltered) - ImVector TempBuffer; // temporary buffer for callback and other other operations. size=capacity. - int CurLenA, CurLenW; // we need to maintain our buffer length in both UTF-8 and wchar format. + ImVector TextA; // temporary UTF8 buffer for callbacks and other operations. this is not updated in every code-path! size=capacity. + ImVector InitialTextA; // backup of end-user buffer at the time of focus (in UTF-8, unaltered) + bool TextAIsValid; // temporary UTF8 buffer is not initially valid before we make the widget active (until then we pull the data from user argument) int BufCapacityA; // end-user buffer capacity - float ScrollX; - ImGuiStb::STB_TexteditState StbState; - float CursorAnim; - bool CursorFollow; - bool SelectedAllMouseLock; + float ScrollX; // horizontal scrolling/offset + ImStb::STB_TexteditState Stb; // state for stb_textedit.h + float CursorAnim; // timer for cursor blink, reset on every user action so the cursor reappears immediately + bool CursorFollow; // set when we want scrolling to follow the current cursor position (not always!) + bool SelectedAllMouseLock; // after a double-click to select all, we ignore further mouse drags to update selection // Temporarily set when active ImGuiInputTextFlags UserFlags; @@ -477,11 +600,14 @@ struct IMGUI_API ImGuiInputTextState void* UserCallbackData; ImGuiInputTextState() { memset(this, 0, sizeof(*this)); } + void ClearFreeMemory() { TextW.clear(); TextA.clear(); InitialTextA.clear(); } void CursorAnimReset() { CursorAnim = -0.30f; } // After a user-input the cursor stays on for a while without blinking - void CursorClamp() { StbState.cursor = ImMin(StbState.cursor, CurLenW); StbState.select_start = ImMin(StbState.select_start, CurLenW); StbState.select_end = ImMin(StbState.select_end, CurLenW); } - bool HasSelection() const { return StbState.select_start != StbState.select_end; } - void ClearSelection() { StbState.select_start = StbState.select_end = StbState.cursor; } - void SelectAll() { StbState.select_start = 0; StbState.cursor = StbState.select_end = CurLenW; StbState.has_preferred_x = false; } + void CursorClamp() { Stb.cursor = ImMin(Stb.cursor, CurLenW); Stb.select_start = ImMin(Stb.select_start, CurLenW); Stb.select_end = ImMin(Stb.select_end, CurLenW); } + bool HasSelection() const { return Stb.select_start != Stb.select_end; } + void ClearSelection() { Stb.select_start = Stb.select_end = Stb.cursor; } + void SelectAll() { Stb.select_start = 0; Stb.cursor = Stb.select_end = CurLenW; Stb.has_preferred_x = 0; } + int GetUndoAvailCount() const { return Stb.undostate.undo_point; } + int GetRedoAvailCount() const { return STB_TEXTEDIT_UNDOSTATECOUNT - Stb.undostate.redo_point; } void OnKeyPressed(int key); // Cannot be inline because we call in code in stb_textedit.h implementation }; @@ -499,8 +625,8 @@ struct ImGuiWindowSettings struct ImGuiSettingsHandler { - const char* TypeName; // Short description stored in .ini file. Disallowed characters: '[' ']' - ImGuiID TypeHash; // == ImHash(TypeName, 0, 0) + const char* TypeName; // Short description stored in .ini file. Disallowed characters: '[' ']' + ImGuiID TypeHash; // == ImHashStr(TypeName, 0, 0) void* (*ReadOpenFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, const char* name); // Read: Called when entering into a new ini entry e.g. "[Window][Name]" void (*ReadLineFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, void* entry, const char* line); // Read: Called for every line of text within an ini entry void (*WriteAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* out_buf); // Write: Output every entries into 'out_buf' @@ -516,7 +642,7 @@ struct ImGuiPopupRef ImGuiWindow* Window; // Resolved on BeginPopup() - may stay unresolved if user never calls OpenPopup() ImGuiWindow* ParentWindow; // Set on OpenPopup() int OpenFrameCount; // Set on OpenPopup() - ImGuiID OpenParentId; // Set on OpenPopup(), we need this to differenciate multiple menu sets from each others (e.g. inside menu bar vs loose menu items) + ImGuiID OpenParentId; // Set on OpenPopup(), we need this to differentiate multiple menu sets from each others (e.g. inside menu bar vs loose menu items) ImVec2 OpenPopupPos; // Set on OpenPopup(), preferred popup position (typically == OpenMousePos when using mouse) ImVec2 OpenMousePos; // Set on OpenPopup(), copy of mouse position at the time of opening popup }; @@ -590,6 +716,7 @@ struct ImDrawDataBuilder struct ImGuiNavMoveResult { ImGuiID ID; // Best candidate + ImGuiID SelectScopeId;// Best candidate window current selectable group ID ImGuiWindow* Window; // Best candidate window float DistBox; // Best candidate box distance to current NavId float DistCenter; // Best candidate center distance to current NavId @@ -597,7 +724,7 @@ struct ImGuiNavMoveResult ImRect RectRel; // Best candidate bounding box in window relative space ImGuiNavMoveResult() { Clear(); } - void Clear() { ID = 0; Window = NULL; DistBox = DistCenter = DistAxial = FLT_MAX; RectRel = ImRect(); } + void Clear() { ID = SelectScopeId = 0; Window = NULL; DistBox = DistCenter = DistAxial = FLT_MAX; RectRel = ImRect(); } }; // Storage for SetNexWindow** functions @@ -640,11 +767,34 @@ struct ImGuiNextWindowData } }; +//----------------------------------------------------------------------------- +// Tabs +//----------------------------------------------------------------------------- + +struct ImGuiTabBarSortItem +{ + int Index; + float Width; +}; + +struct ImGuiTabBarRef +{ + ImGuiTabBar* Ptr; // Either field can be set, not both. Dock node tab bars are loose while BeginTabBar() ones are in a pool. + int IndexInMainPool; + + ImGuiTabBarRef(ImGuiTabBar* ptr) { Ptr = ptr; IndexInMainPool = -1; } + ImGuiTabBarRef(int index_in_main_pool) { Ptr = NULL; IndexInMainPool = index_in_main_pool; } +}; + +//----------------------------------------------------------------------------- // Main imgui context +//----------------------------------------------------------------------------- + struct ImGuiContext { bool Initialized; - bool FrameScopeActive; // Set by NewFrame(), cleared by EndFrame()/Render() + bool FrameScopeActive; // Set by NewFrame(), cleared by EndFrame() + bool FrameScopePushedImplicitWindow; // Set by NewFrame(), cleared by EndFrame() bool FontAtlasOwnedByContext; // Io.Fonts-> is owned by the ImGuiContext and will be destructed along with it. ImGuiIO IO; ImGuiStyle Style; @@ -677,10 +827,12 @@ struct ImGuiContext float ActiveIdTimer; bool ActiveIdIsJustActivated; // Set at the time of activation for one frame bool ActiveIdAllowOverlap; // Active widget allows another widget to steal active id (generally for overlapping widgets, but not always) + bool ActiveIdHasBeenPressed; // Track whether the active id led to a press (this is to allow changing between PressOnClick and PressOnRelease without pressing twice). Used by range_select branch. bool ActiveIdHasBeenEdited; // Was the value associated to the widget Edited over the course of the Active state. bool ActiveIdPreviousFrameIsAlive; bool ActiveIdPreviousFrameHasBeenEdited; int ActiveIdAllowNavDirFlags; // Active widget allows using directional navigation (e.g. can activate a button and move away from it) + int ActiveIdBlockNavInputFlags; ImVec2 ActiveIdClickOffset; // Clicked offset from upper-left corner, if applicable (currently only set by ButtonBehavior) ImGuiWindow* ActiveIdWindow; ImGuiWindow* ActiveIdPreviousFrameWindow; @@ -693,7 +845,7 @@ struct ImGuiContext ImVector StyleModifiers; // Stack for PushStyleVar()/PopStyleVar() ImVector FontStack; // Stack for PushFont()/PopFont() ImVector OpenPopupStack; // Which popups are open (persistent) - ImVector CurrentPopupStack; // Which level of BeginPopup() we are in (reset every frame) + ImVector BeginPopupStack; // Which level of BeginPopup() we are in (reset every frame) ImGuiNextWindowData NextWindowData; // Storage for SetNextWindow** functions bool NextTreeNodeOpenVal; // Storage for SetNextTreeNode** functions ImGuiCond NextTreeNodeOpenCond; @@ -706,9 +858,10 @@ struct ImGuiContext ImGuiID NavActivatePressedId; // ~~ IsNavInputPressed(ImGuiNavInput_Activate) ? NavId : 0 ImGuiID NavInputId; // ~~ IsNavInputPressed(ImGuiNavInput_Input) ? NavId : 0 ImGuiID NavJustTabbedId; // Just tabbed to this id. - ImGuiID NavJustMovedToId; // Just navigated to this id (result of a successfully MoveRequest) - ImGuiID NavNextActivateId; // Set by ActivateItem(), queued until next frame - ImGuiInputSource NavInputSource; // Keyboard or Gamepad mode? + ImGuiID NavJustMovedToId; // Just navigated to this id (result of a successfully MoveRequest). + ImGuiID NavJustMovedToSelectScopeId; // Just navigated to this select scope id (result of a successfully MoveRequest). + ImGuiID NavNextActivateId; // Set by ActivateItem(), queued until next frame. + ImGuiInputSource NavInputSource; // Keyboard or Gamepad mode? THIS WILL ONLY BE None or NavGamepad or NavKeyboard. ImRect NavScoringRectScreen; // Rectangle used for scoring, in screen space. Based of window->DC.NavRefRectRel[], modified for directional navigation scoring. int NavScoringCount; // Metrics for debugging ImGuiWindow* NavWindowingTarget; // When selecting a window (holding Menu+FocusPrev/Next, or equivalent of CTRL-TAB) this window is temporarily displayed front-most. @@ -717,7 +870,7 @@ struct ImGuiContext float NavWindowingTimer; float NavWindowingHighlightAlpha; bool NavWindowingToggleLayer; - int NavLayer; // Layer we are navigating on. For now the system is hard-coded for 0=main contents and 1=menu/title bar, may expose layers later. + ImGuiNavLayer NavLayer; // Layer we are navigating on. For now the system is hard-coded for 0=main contents and 1=menu/title bar, may expose layers later. int NavIdTabCounter; // == NavWindow->DC.FocusIdxTabCounter at time of NavId processing bool NavIdIsAlive; // Nav widget has been seen this frame ~~ NavRefRectRel is valid bool NavMousePosDirty; // When set we will update mouse position if (io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) if set (NB: this not enabled by default) @@ -738,11 +891,21 @@ struct ImGuiContext ImGuiNavMoveResult NavMoveResultLocalVisibleSet; // Best move request candidate within NavWindow that are mostly visible (when using ImGuiNavMoveFlags_AlsoScoreVisibleSet flag) ImGuiNavMoveResult NavMoveResultOther; // Best move request candidate within NavWindow's flattened hierarchy (when using ImGuiWindowFlags_NavFlattened flag) + // Tabbing system (older than Nav, active even if Nav is disabled. FIXME-NAV: This needs a redesign!) + ImGuiWindow* FocusRequestCurrWindow; // + ImGuiWindow* FocusRequestNextWindow; // + int FocusRequestCurrCounterAll; // Any item being requested for focus, stored as an index (we on layout to be stable between the frame pressing TAB and the next frame, semi-ouch) + int FocusRequestCurrCounterTab; // Tab item being requested for focus, stored as an index + int FocusRequestNextCounterAll; // Stored for next frame + int FocusRequestNextCounterTab; // " + bool FocusTabPressed; // + // Render ImDrawData DrawData; // Main ImDrawData instance to pass render information to the user ImDrawDataBuilder DrawDataBuilder; float DimBgRatio; // 0.0..1.0 animation when fading in a dimming background (for modal window and CTRL+TAB list) - ImDrawList OverlayDrawList; // Optional software render of mouse cursors, if io.MouseDrawCursor is set + a few debug overlays + ImDrawList BackgroundDrawList; // First draw list to be rendered. + ImDrawList ForegroundDrawList; // Last draw list to be rendered. This is where we the render software mouse cursor (if io.MouseDrawCursor is set) and most debug overlays. ImGuiMouseCursor MouseCursor; // Drag and Drop @@ -762,6 +925,12 @@ struct ImGuiContext ImVector DragDropPayloadBufHeap; // We don't expose the ImVector<> directly unsigned char DragDropPayloadBufLocal[8]; // Local buffer for small payloads + // Tab bars + ImPool TabBars; + ImGuiTabBar* CurrentTabBar; + ImVector CurrentTabBarStack; + ImVector TabSortByWidthBuffer; + // Widget state ImGuiInputTextState InputTextState; ImFont InputTextPasswordFont; @@ -774,7 +943,14 @@ struct ImGuiContext ImVec2 ScrollbarClickDeltaToGrabCenter; // Distance between mouse and center of grab box, normalized in parent space. Use storage? int TooltipOverrideCount; ImVector PrivateClipboard; // If no custom clipboard handler is defined - ImVec2 PlatformImePos, PlatformImeLastPos; // Cursor position request & last passed to the OS Input Method Editor + + // Range-Select/Multi-Select + // [This is unused in this branch, but left here to facilitate merging/syncing multiple branches] + ImGuiID MultiSelectScopeId; + + // Platform support + ImVec2 PlatformImePos; // Cursor position request & last passed to the OS Input Method Editor + ImVec2 PlatformImeLastPos; // Settings bool SettingsLoaded; @@ -785,10 +961,14 @@ struct ImGuiContext // Logging bool LogEnabled; + ImGuiLogType LogType; FILE* LogFile; // If != NULL log to stdout/ file - ImGuiTextBuffer LogClipboard; // Accumulation buffer when log to clipboard. This is pointer so our GImGui static constructor doesn't call heap allocators. - int LogStartDepth; - int LogAutoExpandMaxDepth; + ImGuiTextBuffer LogBuffer; // Accumulation buffer when log to clipboard. This is pointer so our GImGui static constructor doesn't call heap allocators. + float LogLinePosY; + bool LogLineFirstItem; + int LogDepthRef; + int LogDepthToExpand; + int LogDepthToExpandDefault; // Default/stored value for LogDepthMaxExpand if not specified in the LogXXX function call. // Misc float FramerateSecPerFrame[120]; // Calculate estimate of framerate for user over the last 2 seconds. @@ -799,16 +979,16 @@ struct ImGuiContext int WantTextInputNextFrame; char TempBuffer[1024*3+1]; // Temporary text buffer - ImGuiContext(ImFontAtlas* shared_font_atlas) : OverlayDrawList(NULL) + ImGuiContext(ImFontAtlas* shared_font_atlas) : BackgroundDrawList(NULL), ForegroundDrawList(NULL) { Initialized = false; - FrameScopeActive = false; + FrameScopeActive = FrameScopePushedImplicitWindow = false; Font = NULL; FontSize = FontBaseSize = 0.0f; FontAtlasOwnedByContext = shared_font_atlas ? false : true; IO.Fonts = shared_font_atlas ? shared_font_atlas : IM_NEW(ImFontAtlas)(); - Time = 0.0; + Time = 0.0f; FrameCount = 0; FrameCountEnded = FrameCountRendered = -1; WindowsActiveCount = 0; @@ -825,10 +1005,12 @@ struct ImGuiContext ActiveIdTimer = 0.0f; ActiveIdIsJustActivated = false; ActiveIdAllowOverlap = false; + ActiveIdHasBeenPressed = false; ActiveIdHasBeenEdited = false; ActiveIdPreviousFrameIsAlive = false; ActiveIdPreviousFrameHasBeenEdited = false; - ActiveIdAllowNavDirFlags = 0; + ActiveIdAllowNavDirFlags = 0x00; + ActiveIdBlockNavInputFlags = 0x00; ActiveIdClickOffset = ImVec2(-1,-1); ActiveIdWindow = ActiveIdPreviousFrameWindow = NULL; ActiveIdSource = ImGuiInputSource_None; @@ -841,14 +1023,14 @@ struct ImGuiContext NavWindow = NULL; NavId = NavActivateId = NavActivateDownId = NavActivatePressedId = NavInputId = 0; - NavJustTabbedId = NavJustMovedToId = NavNextActivateId = 0; + NavJustTabbedId = NavJustMovedToId = NavJustMovedToSelectScopeId = NavNextActivateId = 0; NavInputSource = ImGuiInputSource_None; NavScoringRectScreen = ImRect(); NavScoringCount = 0; NavWindowingTarget = NavWindowingTargetAnim = NavWindowingList = NULL; NavWindowingTimer = NavWindowingHighlightAlpha = 0.0f; NavWindowingToggleLayer = false; - NavLayer = 0; + NavLayer = ImGuiNavLayer_Main; NavIdTabCounter = INT_MAX; NavIdIsAlive = false; NavMousePosDirty = false; @@ -864,9 +1046,16 @@ struct ImGuiContext NavMoveRequestForward = ImGuiNavForward_None; NavMoveDir = NavMoveDirLast = NavMoveClipDir = ImGuiDir_None; + FocusRequestCurrWindow = FocusRequestNextWindow = NULL; + FocusRequestCurrCounterAll = FocusRequestCurrCounterTab = INT_MAX; + FocusRequestNextCounterAll = FocusRequestNextCounterTab = INT_MAX; + FocusTabPressed = false; + DimBgRatio = 0.0f; - OverlayDrawList._Data = &DrawListSharedData; - OverlayDrawList._OwnerName = "##Overlay"; // Give it a name for debugging + BackgroundDrawList._Data = &DrawListSharedData; + BackgroundDrawList._OwnerName = "##Background"; // Give it a name for debugging + ForegroundDrawList._Data = &DrawListSharedData; + ForegroundDrawList._OwnerName = "##Foreground"; // Give it a name for debugging MouseCursor = ImGuiMouseCursor_Arrow; DragDropActive = DragDropWithinSourceOrTarget = false; @@ -880,6 +1069,8 @@ struct ImGuiContext DragDropAcceptFrameCount = -1; memset(DragDropPayloadBufLocal, 0, sizeof(DragDropPayloadBufLocal)); + CurrentTabBar = NULL; + ScalarAsInputTextId = 0; ColorEditOptions = ImGuiColorEditFlags__OptionsDefault; DragCurrentAccumDirty = false; @@ -887,15 +1078,21 @@ struct ImGuiContext DragSpeedDefaultRatio = 1.0f / 100.0f; ScrollbarClickDeltaToGrabCenter = ImVec2(0.0f, 0.0f); TooltipOverrideCount = 0; + + MultiSelectScopeId = 0; + PlatformImePos = PlatformImeLastPos = ImVec2(FLT_MAX, FLT_MAX); SettingsLoaded = false; SettingsDirtyTimer = 0.0f; LogEnabled = false; + LogType = ImGuiLogType_None; LogFile = NULL; - LogStartDepth = 0; - LogAutoExpandMaxDepth = 2; + LogLinePosY = FLT_MAX; + LogLineFirstItem = false; + LogDepthRef = 0; + LogDepthToExpand = LogDepthToExpandDefault = 2; memset(FramerateSecPerFrame, 0, sizeof(FramerateSecPerFrame)); FramerateSecPerFrameIdx = 0; @@ -905,18 +1102,9 @@ struct ImGuiContext } }; -// Transient per-window flags, reset at the beginning of the frame. For child window, inherited from parent on first Begin(). -// This is going to be exposed in imgui.h when stabilized enough. -enum ImGuiItemFlags_ -{ - ImGuiItemFlags_NoTabStop = 1 << 0, // false - ImGuiItemFlags_ButtonRepeat = 1 << 1, // false // Button() will return true multiple times based on io.KeyRepeatDelay and io.KeyRepeatRate settings. - ImGuiItemFlags_Disabled = 1 << 2, // false // [BETA] Disable interactions but doesn't affect visuals yet. See github.com/ocornut/imgui/issues/211 - ImGuiItemFlags_NoNav = 1 << 3, // false - ImGuiItemFlags_NoNavDefaultFocus = 1 << 4, // false - ImGuiItemFlags_SelectableDontClosePopup = 1 << 5, // false // MenuItem/Selectable() automatically closes current Popup window - ImGuiItemFlags_Default_ = 0 -}; +//----------------------------------------------------------------------------- +// ImGuiWindow +//----------------------------------------------------------------------------- // Transient per-window data, reset at the beginning of the frame. This used to be called ImGuiDrawContext, hence the DC variable name in ImGuiWindow. // FIXME: That's theory, in practice the delimitation between ImGuiWindow and ImGuiWindowTempData is quite tenuous and could be reconsidered. @@ -930,25 +1118,26 @@ struct IMGUI_API ImGuiWindowTempData float CurrentLineTextBaseOffset; ImVec2 PrevLineSize; float PrevLineTextBaseOffset; - float LogLinePosY; int TreeDepth; ImU32 TreeDepthMayJumpToParentOnPop; // Store a copy of !g.NavIdIsAlive for TreeDepth 0..31 ImGuiID LastItemId; ImGuiItemStatusFlags LastItemStatusFlags; ImRect LastItemRect; // Interaction rect ImRect LastItemDisplayRect; // End-user display rect (only valid if LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) - bool NavHideHighlightOneFrame; - bool NavHasScroll; // Set when scrolling can be used (ScrollMax > 0.0f) - int NavLayerCurrent; // Current layer, 0..31 (we currently only use 0..1) + ImGuiNavLayer NavLayerCurrent; // Current layer, 0..31 (we currently only use 0..1) int NavLayerCurrentMask; // = (1 << NavLayerCurrent) used by ItemAdd prior to clipping. int NavLayerActiveMask; // Which layer have been written to (result from previous frame) int NavLayerActiveMaskNext; // Which layer have been written to (buffer for current frame) + bool NavHideHighlightOneFrame; + bool NavHasScroll; // Set when scrolling can be used (ScrollMax > 0.0f) bool MenuBarAppending; // FIXME: Remove this ImVec2 MenuBarOffset; // MenuBarOffset.x is sort of equivalent of a per-layer CursorPos.x, saved/restored as we switch to the menu bar. The only situation when MenuBarOffset.y is > 0 if when (SafeAreaPadding.y > FramePadding.y), often used on TVs. ImVector ChildWindows; ImGuiStorage* StateStorage; ImGuiLayoutType LayoutType; ImGuiLayoutType ParentLayoutType; // Layout type of parent window at the time of Begin() + int FocusCounterAll; // Counter for focus/tabbing system. Start at -1 and increase as assigned via FocusableItemRegister() (FIXME-NAV: Needs redesign) + int FocusCounterTab; // (same, but only count widgets which you can Tab through) // We store the current settings outside of the vectors to increase memory locality (reduce cache misses). The vectors are rarely modified. Also it allows us to not heap allocate for short-lived windows which are not using those settings. ImGuiItemFlags ItemFlags; // == ItemFlagsStack.back() [empty == ImGuiItemFlags_Default] @@ -958,7 +1147,7 @@ struct IMGUI_API ImGuiWindowTempData ImVector ItemWidthStack; ImVector TextWrapPosStack; ImVectorGroupStack; - int StackSizesBackup[6]; // Store size of various stacks for asserting + short StackSizesBackup[6]; // Store size of various stacks for asserting ImVec1 Indent; // Indentation / start position from left of window (increased by TreePush/TreePop, etc.) ImVec1 GroupOffset; @@ -970,23 +1159,24 @@ struct IMGUI_API ImGuiWindowTempData CursorPos = CursorPosPrevLine = CursorStartPos = CursorMaxPos = ImVec2(0.0f, 0.0f); CurrentLineSize = PrevLineSize = ImVec2(0.0f, 0.0f); CurrentLineTextBaseOffset = PrevLineTextBaseOffset = 0.0f; - LogLinePosY = -1.0f; TreeDepth = 0; TreeDepthMayJumpToParentOnPop = 0x00; LastItemId = 0; LastItemStatusFlags = 0; LastItemRect = LastItemDisplayRect = ImRect(); + NavLayerActiveMask = NavLayerActiveMaskNext = 0x00; + NavLayerCurrent = ImGuiNavLayer_Main; + NavLayerCurrentMask = (1 << ImGuiNavLayer_Main); NavHideHighlightOneFrame = false; NavHasScroll = false; - NavLayerActiveMask = NavLayerActiveMaskNext = 0x00; - NavLayerCurrent = 0; - NavLayerCurrentMask = 1 << 0; MenuBarAppending = false; MenuBarOffset = ImVec2(0.0f, 0.0f); StateStorage = NULL; LayoutType = ParentLayoutType = ImGuiLayoutType_Vertical; - ItemWidth = 0.0f; + FocusCounterAll = FocusCounterTab = -1; + ItemFlags = ImGuiItemFlags_Default_; + ItemWidth = 0.0f; TextWrapPos = -1.0f; memset(StackSizesBackup, 0, sizeof(StackSizesBackup)); @@ -1012,6 +1202,7 @@ struct IMGUI_API ImGuiWindow ImVec2 WindowPadding; // Window padding at the time of begin. float WindowRounding; // Window rounding at the time of begin. float WindowBorderSize; // Window border size at the time of begin. + int NameBufLen; // Size of buffer storing Name. May be larger than strlen(Name)! ImGuiID MoveId; // == window->GetID("#MOVE") ImGuiID ChildId; // ID of corresponding item in parent window (for navigation to return from child window to parent window) ImVec2 Scroll; @@ -1028,9 +1219,10 @@ struct IMGUI_API ImGuiWindow bool Appearing; // Set during the frame where the window is appearing (or re-appearing) bool Hidden; // Do not display (== (HiddenFramesForResize > 0) || bool HasCloseButton; // Set when the window has a close button (p_open != NULL) - int BeginCount; // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs) - int BeginOrderWithinParent; // Order within immediate parent window, if we are a child window. Otherwise 0. - int BeginOrderWithinContext; // Order within entire imgui context. This is mostly used for debugging submission order related issues. + signed char ResizeBorderHeld; // Current border being held for resize (-1: none, otherwise 0-3) + short BeginCount; // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs) + short BeginOrderWithinParent; // Order within immediate parent window, if we are a child window. Otherwise 0. + short BeginOrderWithinContext; // Order within entire imgui context. This is mostly used for debugging submission order related issues. ImGuiID PopupId; // ID in the popup stack when this window is used as a popup/menu (because we use generic Name/ID for recycling) int AutoFitFramesX, AutoFitFramesY; bool AutoFitOnlyGrows; @@ -1066,17 +1258,8 @@ struct IMGUI_API ImGuiWindow ImGuiWindow* RootWindowForNav; // Point to ourself or first ancestor which doesn't have the NavFlattened flag. ImGuiWindow* NavLastChildNavWindow; // When going to the menu bar, we remember the child window we came from. (This could probably be made implicit if we kept g.Windows sorted by last focused including child window.) - ImGuiID NavLastIds[2]; // Last known NavId for this window, per layer (0/1) - ImRect NavRectRel[2]; // Reference rectangle, in window relative space - - // Navigation / Focus - // FIXME-NAV: Merge all this with the new Nav system, at least the request variables should be moved to ImGuiContext - int FocusIdxAllCounter; // Start at -1 and increase as assigned via FocusItemRegister() - int FocusIdxTabCounter; // (same, but only count widgets which you can Tab through) - int FocusIdxAllRequestCurrent; // Item being requested for focus - int FocusIdxTabRequestCurrent; // Tab-able item being requested for focus - int FocusIdxAllRequestNext; // Item being requested for focus, for next update (relies on layout to be stable between the frame pressing TAB and the next frame) - int FocusIdxTabRequestNext; // " + ImGuiID NavLastIds[ImGuiNavLayer_COUNT]; // Last known NavId for this window, per layer (0/1) + ImRect NavRectRel[ImGuiNavLayer_COUNT]; // Reference rectangle, in window relative space public: ImGuiWindow(ImGuiContext* context, const char* name); @@ -1110,6 +1293,73 @@ struct ImGuiItemHoveredDataBackup void Restore() const { ImGuiWindow* window = GImGui->CurrentWindow; window->DC.LastItemId = LastItemId; window->DC.LastItemStatusFlags = LastItemStatusFlags; window->DC.LastItemRect = LastItemRect; window->DC.LastItemDisplayRect = LastItemDisplayRect; } }; +//----------------------------------------------------------------------------- +// Tab bar, tab item +//----------------------------------------------------------------------------- + +enum ImGuiTabBarFlagsPrivate_ +{ + ImGuiTabBarFlags_DockNode = 1 << 20, // Part of a dock node [we don't use this in the master branch but it facilitate branch syncing to keep this around] + ImGuiTabBarFlags_IsFocused = 1 << 21, + ImGuiTabBarFlags_SaveSettings = 1 << 22 // FIXME: Settings are handled by the docking system, this only request the tab bar to mark settings dirty when reordering tabs +}; + +enum ImGuiTabItemFlagsPrivate_ +{ + ImGuiTabItemFlags_NoCloseButton = 1 << 20 // Store whether p_open is set or not, which we need to recompute WidthContents during layout. +}; + +// Storage for one active tab item (sizeof() 26~32 bytes) +struct ImGuiTabItem +{ + ImGuiID ID; + ImGuiTabItemFlags Flags; + int LastFrameVisible; + int LastFrameSelected; // This allows us to infer an ordered list of the last activated tabs with little maintenance + int NameOffset; // When Window==NULL, offset to name within parent ImGuiTabBar::TabsNames + float Offset; // Position relative to beginning of tab + float Width; // Width currently displayed + float WidthContents; // Width of actual contents, stored during BeginTabItem() call + + ImGuiTabItem() { ID = 0; Flags = 0; LastFrameVisible = LastFrameSelected = -1; NameOffset = -1; Offset = Width = WidthContents = 0.0f; } +}; + +// Storage for a tab bar (sizeof() 92~96 bytes) +struct ImGuiTabBar +{ + ImVector Tabs; + ImGuiID ID; // Zero for tab-bars used by docking + ImGuiID SelectedTabId; // Selected tab + ImGuiID NextSelectedTabId; + ImGuiID VisibleTabId; // Can occasionally be != SelectedTabId (e.g. when previewing contents for CTRL+TAB preview) + int CurrFrameVisible; + int PrevFrameVisible; + ImRect BarRect; + float ContentsHeight; + float OffsetMax; // Distance from BarRect.Min.x, locked during layout + float OffsetNextTab; // Distance from BarRect.Min.x, incremented with each BeginTabItem() call, not used if ImGuiTabBarFlags_Reorderable if set. + float ScrollingAnim; + float ScrollingTarget; + float ScrollingTargetDistToVisibility; + float ScrollingSpeed; + ImGuiTabBarFlags Flags; + ImGuiID ReorderRequestTabId; + int ReorderRequestDir; + bool WantLayout; + bool VisibleTabWasSubmitted; + short LastTabItemIdx; // For BeginTabItem()/EndTabItem() + ImVec2 FramePadding; // style.FramePadding locked at the time of BeginTabBar() + ImGuiTextBuffer TabsNames; // For non-docking tab bar we re-append names in a contiguous buffer. + + ImGuiTabBar(); + int GetTabOrder(const ImGuiTabItem* tab) const { return Tabs.index_from_ptr(tab); } + const char* GetTabName(const ImGuiTabItem* tab) const + { + IM_ASSERT(tab->NameOffset != -1 && tab->NameOffset < TabsNames.Buf.Size); + return TabsNames.Buf.Data + tab->NameOffset; + } +}; + //----------------------------------------------------------------------------- // Internal API // No guarantee of forward compatibility here. @@ -1139,6 +1389,10 @@ namespace ImGui IMGUI_API float GetWindowScrollMaxX(ImGuiWindow* window); IMGUI_API float GetWindowScrollMaxY(ImGuiWindow* window); IMGUI_API ImRect GetWindowAllowedExtentRect(ImGuiWindow* window); + IMGUI_API void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond); + IMGUI_API void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond); + IMGUI_API void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond); + IMGUI_API void SetCurrentFont(ImFont* font); inline ImFont* GetDefaultFont() { ImGuiContext& g = *GImGui; return g.IO.FontDefault ? g.IO.FontDefault : g.IO.Fonts->Fonts[0]; } @@ -1149,13 +1403,15 @@ namespace ImGui // NewFrame IMGUI_API void UpdateHoveredWindowAndCaptureFlags(); IMGUI_API void StartMouseMovingWindow(ImGuiWindow* window); - IMGUI_API void UpdateMouseMovingWindow(); + IMGUI_API void UpdateMouseMovingWindowNewFrame(); + IMGUI_API void UpdateMouseMovingWindowEndFrame(); // Settings IMGUI_API void MarkIniSettingsDirty(); IMGUI_API void MarkIniSettingsDirty(ImGuiWindow* window); IMGUI_API ImGuiWindowSettings* CreateNewWindowSettings(const char* name); IMGUI_API ImGuiWindowSettings* FindWindowSettings(ImGuiID id); + IMGUI_API ImGuiWindowSettings* FindOrCreateWindowSettings(const char* name); IMGUI_API ImGuiSettingsHandler* FindSettingsHandler(const char* type_name); // Basic Accessors @@ -1176,7 +1432,7 @@ namespace ImGui IMGUI_API bool ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb = NULL); IMGUI_API bool ItemHoverable(const ImRect& bb, ImGuiID id); IMGUI_API bool IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged); - IMGUI_API bool FocusableItemRegister(ImGuiWindow* window, ImGuiID id, bool tab_stop = true); // Return true if focus is requested + IMGUI_API bool FocusableItemRegister(ImGuiWindow* window, ImGuiID id); // Return true if focus is requested IMGUI_API void FocusableItemUnregister(ImGuiWindow* window); IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_x, float default_y); IMGUI_API float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x); @@ -1184,12 +1440,15 @@ namespace ImGui IMGUI_API void PushItemFlag(ImGuiItemFlags option, bool enabled); IMGUI_API void PopItemFlag(); + // Logging/Capture + IMGUI_API void LogBegin(ImGuiLogType type, int auto_open_depth); // -> BeginCapture() when we design v2 api, for now stay under the radar by using the old name. + IMGUI_API void LogToBuffer(int auto_open_depth = -1); // Start logging/capturing to internal buffer + // Popups, Modals, Tooltips IMGUI_API void OpenPopupEx(ImGuiID id); - IMGUI_API void ClosePopup(ImGuiID id); - IMGUI_API void ClosePopupToLevel(int remaining); + IMGUI_API void ClosePopupToLevel(int remaining, bool apply_focus_to_window_under); IMGUI_API void ClosePopupsOverWindow(ImGuiWindow* ref_window); - IMGUI_API bool IsPopupOpen(ImGuiID id); + IMGUI_API bool IsPopupOpen(ImGuiID id); // Test for id within current popup stack level (currently begin-ed into); this doesn't scan the whole popup stack! IMGUI_API bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags); IMGUI_API void BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_tooltip = true); IMGUI_API ImGuiWindow* GetFrontMostPopupModal(); @@ -1225,12 +1484,24 @@ namespace ImGui IMGUI_API void EndColumns(); // close columns IMGUI_API void PushColumnClipRect(int column_index = -1); + // Tab Bars + IMGUI_API bool BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& bb, ImGuiTabBarFlags flags); + IMGUI_API ImGuiTabItem* TabBarFindTabByID(ImGuiTabBar* tab_bar, ImGuiID tab_id); + IMGUI_API void TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id); + IMGUI_API void TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab); + IMGUI_API void TabBarQueueChangeTabOrder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int dir); + IMGUI_API bool TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags); + IMGUI_API ImVec2 TabItemCalcSize(const char* label, bool has_close_button); + IMGUI_API void TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImU32 col); + IMGUI_API bool TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImVec2 frame_padding, const char* label, ImGuiID tab_id, ImGuiID close_button_id); + // Render helpers // AVOID USING OUTSIDE OF IMGUI.CPP! NOT FOR PUBLIC CONSUMPTION. THOSE FUNCTIONS ARE A MESS. THEIR SIGNATURE AND BEHAVIOR WILL CHANGE, THEY NEED TO BE REFACTORED INTO SOMETHING DECENT. // NB: All position are in absolute pixels coordinates (we are never using window coordinates internally) IMGUI_API void RenderText(ImVec2 pos, const char* text, const char* text_end = NULL, bool hide_text_after_hash = true); IMGUI_API void RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width); IMGUI_API void RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0,0), const ImRect* clip_rect = NULL); + IMGUI_API void RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL); IMGUI_API void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border = true, float rounding = 0.0f); IMGUI_API void RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding = 0.0f); IMGUI_API void RenderColorRectWithAlphaCheckerboard(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, float grid_step, ImVec2 grid_off, float rounding = 0.0f, int rounding_corners_flags = ~0); @@ -1245,13 +1516,16 @@ namespace ImGui IMGUI_API void RenderMouseCursor(ImDrawList* draw_list, ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor = ImGuiMouseCursor_Arrow); IMGUI_API void RenderArrowPointingAt(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, ImGuiDir direction, ImU32 col); IMGUI_API void RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float x_start_norm, float x_end_norm, float rounding); + IMGUI_API void RenderPixelEllipsis(ImDrawList* draw_list, ImVec2 pos, int count, ImU32 col); // Widgets + IMGUI_API void TextEx(const char* text, const char* text_end = NULL, ImGuiTextFlags flags = 0); IMGUI_API bool ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0,0), ImGuiButtonFlags flags = 0); IMGUI_API bool CloseButton(ImGuiID id, const ImVec2& pos, float radius); IMGUI_API bool CollapseButton(ImGuiID id, const ImVec2& pos); IMGUI_API bool ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size_arg, ImGuiButtonFlags flags); - IMGUI_API void Scrollbar(ImGuiLayoutType direction); + IMGUI_API void Scrollbar(ImGuiAxis axis); + IMGUI_API ImGuiID GetScrollbarID(ImGuiWindow* window, ImGuiAxis axis); IMGUI_API void VerticalSeparator(); // Vertical separator, for menu bars (use current line height). Not exposed because it is misleading and it doesn't have an effect on regular layout. // Widgets low-level behaviors @@ -1263,16 +1537,16 @@ namespace ImGui IMGUI_API bool TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags = 0); // Consume previous SetNextTreeNodeOpened() data, if any. May return true when logging IMGUI_API void TreePushRawID(ImGuiID id); - // Template functions are instantiated in imgui_widgets.cpp for a finite number of types. + // Template functions are instantiated in imgui_widgets.cpp for a finite number of types. // To use them externally (for custom widget) you may need an "extern template" statement in your code in order to link to existing instances and silence Clang warnings (see #2036). // e.g. " extern template IMGUI_API float RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, float v); " - template IMGUI_API bool DragBehaviorT(ImGuiDataType data_type, T* v, float v_speed, const T v_min, const T v_max, const char* format, float power, ImGuiDragFlags flags); - template IMGUI_API bool SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, T* v, const T v_min, const T v_max, const char* format, float power, ImGuiSliderFlags flags, ImRect* out_grab_bb); + template IMGUI_API bool DragBehaviorT(ImGuiDataType data_type, T* v, float v_speed, T v_min, T v_max, const char* format, float power, ImGuiDragFlags flags); + template IMGUI_API bool SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, T* v, T v_min, T v_max, const char* format, float power, ImGuiSliderFlags flags, ImRect* out_grab_bb); template IMGUI_API float SliderCalcRatioFromValueT(ImGuiDataType data_type, T v, T v_min, T v_max, float power, float linear_zero_pos); template IMGUI_API T RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, T v); // InputText - IMGUI_API bool InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); + IMGUI_API bool InputTextEx(const char* label, const char* hint, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); IMGUI_API bool InputScalarAsWidgetReplacement(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* data_ptr, const char* format); // Color @@ -1281,7 +1555,7 @@ namespace ImGui IMGUI_API void ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags flags); // Plot - IMGUI_API void PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size); + IMGUI_API void PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 frame_size); // Shade functions (write over already created vertices) IMGUI_API void ShadeVertsLinearColorGradientKeepAlpha(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, ImVec2 gradient_p0, ImVec2 gradient_p1, ImU32 col0, ImU32 col1); @@ -1293,11 +1567,23 @@ namespace ImGui IMGUI_API bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas); IMGUI_API void ImFontAtlasBuildRegisterDefaultCustomRects(ImFontAtlas* atlas); IMGUI_API void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent); -IMGUI_API void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* spc); +IMGUI_API void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opaque); IMGUI_API void ImFontAtlasBuildFinish(ImFontAtlas* atlas); IMGUI_API void ImFontAtlasBuildMultiplyCalcLookupTable(unsigned char out_table[256], float in_multiply_factor); IMGUI_API void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsigned char* pixels, int x, int y, int w, int h, int stride); +// Test engine hooks (imgui-test) +//#define IMGUI_ENABLE_TEST_ENGINE +#ifdef IMGUI_ENABLE_TEST_ENGINE +extern void ImGuiTestEngineHook_PreNewFrame(ImGuiContext* ctx); +extern void ImGuiTestEngineHook_PostNewFrame(ImGuiContext* ctx); +extern void ImGuiTestEngineHook_ItemAdd(ImGuiContext* ctx, const ImRect& bb, ImGuiID id); +extern void ImGuiTestEngineHook_ItemInfo(ImGuiContext* ctx, ImGuiID id, const char* label, ImGuiItemStatusFlags flags); +#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID, _LABEL, _FLAGS) ImGuiTestEngineHook_ItemInfo(&g, _ID, _LABEL, _FLAGS) // Register status flags +#else +#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID, _LABEL, _FLAGS) do { } while (0) +#endif + #ifdef __clang__ #pragma clang diagnostic pop #endif diff --git a/src/lib/geogram_gfx/third_party/ImGui/imgui_widgets.cpp b/src/lib/geogram_gfx/third_party/ImGui/imgui_widgets.cpp index 7a24cfa6..06ddba4c 100644 --- a/src/lib/geogram_gfx/third_party/ImGui/imgui_widgets.cpp +++ b/src/lib/geogram_gfx/third_party/ImGui/imgui_widgets.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.66 +// dear imgui, v1.69 // (widgets code) /* @@ -22,6 +22,8 @@ Index of this file: // [SECTION] Widgets: PlotLines, PlotHistogram // [SECTION] Widgets: Value helpers // [SECTION] Widgets: MenuItem, BeginMenu, EndMenu, etc. +// [SECTION] Widgets: BeginTabBar, EndTabBar, etc. +// [SECTION] Widgets: BeginTabItem, EndTabItem, etc. */ @@ -50,8 +52,16 @@ Index of this file: // Clang/GCC warnings with -Weverything #ifdef __clang__ +#pragma clang diagnostic ignored "-Wold-style-cast" // warning : use of old-style cast // yes, they are more terse. +#pragma clang diagnostic ignored "-Wfloat-equal" // warning : comparing floating point with == or != is unsafe // storing and comparing against same constants (typically 0.0f) is ok. #pragma clang diagnostic ignored "-Wformat-nonliteral" // warning : format string is not a string literal // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code. #pragma clang diagnostic ignored "-Wsign-conversion" // warning : implicit conversion changes signedness // +#if __has_warning("-Wzero-as-null-pointer-constant") +#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning : zero as null pointer constant // some standard header variations use #define NULL 0 +#endif +#if __has_warning("-Wdouble-promotion") +#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double. +#endif #elif defined(__GNUC__) #pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked #if __GNUC__ >= 8 @@ -64,22 +74,30 @@ Index of this file: //------------------------------------------------------------------------- // Those MIN/MAX values are not define because we need to point to them -static const ImS32 IM_S32_MIN = INT_MIN; // (-2147483647 - 1), (0x80000000); -static const ImS32 IM_S32_MAX = INT_MAX; // (2147483647), (0x7FFFFFFF) -static const ImU32 IM_U32_MIN = 0; -static const ImU32 IM_U32_MAX = UINT_MAX; // (0xFFFFFFFF) +static const signed char IM_S8_MIN = -128; +static const signed char IM_S8_MAX = 127; +static const unsigned char IM_U8_MIN = 0; +static const unsigned char IM_U8_MAX = 0xFF; +static const signed short IM_S16_MIN = -32768; +static const signed short IM_S16_MAX = 32767; +static const unsigned short IM_U16_MIN = 0; +static const unsigned short IM_U16_MAX = 0xFFFF; +static const ImS32 IM_S32_MIN = INT_MIN; // (-2147483647 - 1), (0x80000000); +static const ImS32 IM_S32_MAX = INT_MAX; // (2147483647), (0x7FFFFFFF) +static const ImU32 IM_U32_MIN = 0; +static const ImU32 IM_U32_MAX = UINT_MAX; // (0xFFFFFFFF) #ifdef LLONG_MIN -static const ImS64 IM_S64_MIN = LLONG_MIN; // (-9223372036854775807ll - 1ll); -static const ImS64 IM_S64_MAX = LLONG_MAX; // (9223372036854775807ll); +static const ImS64 IM_S64_MIN = LLONG_MIN; // (-9223372036854775807ll - 1ll); +static const ImS64 IM_S64_MAX = LLONG_MAX; // (9223372036854775807ll); #else -static const ImS64 IM_S64_MIN = -9223372036854775807LL - 1; -static const ImS64 IM_S64_MAX = 9223372036854775807LL; +static const ImS64 IM_S64_MIN = -9223372036854775807LL - 1; +static const ImS64 IM_S64_MAX = 9223372036854775807LL; #endif -static const ImU64 IM_U64_MIN = 0; +static const ImU64 IM_U64_MIN = 0; #ifdef ULLONG_MAX -static const ImU64 IM_U64_MAX = ULLONG_MAX; // (0xFFFFFFFFFFFFFFFFull); +static const ImU64 IM_U64_MAX = ULLONG_MAX; // (0xFFFFFFFFFFFFFFFFull); #else -static const ImU64 IM_U64_MAX = (2ULL * 9223372036854775807LL + 1); +static const ImU64 IM_U64_MAX = (2ULL * 9223372036854775807LL + 1); #endif //------------------------------------------------------------------------- @@ -114,7 +132,7 @@ static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const // - BulletTextV() //------------------------------------------------------------------------- -void ImGui::TextUnformatted(const char* text, const char* text_end) +void ImGui::TextEx(const char* text, const char* text_end, ImGuiTextFlags flags) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) @@ -128,7 +146,7 @@ void ImGui::TextUnformatted(const char* text, const char* text_end) const ImVec2 text_pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrentLineTextBaseOffset); const float wrap_pos_x = window->DC.TextWrapPos; - const bool wrap_enabled = wrap_pos_x >= 0.0f; + const bool wrap_enabled = (wrap_pos_x >= 0.0f); if (text_end - text > 2000 && !wrap_enabled) { // Long text! @@ -138,71 +156,68 @@ void ImGui::TextUnformatted(const char* text, const char* text_end) // - We use memchr(), pay attention that well optimized versions of those str/mem functions are much faster than a casually written loop. const char* line = text; const float line_height = GetTextLineHeight(); - const ImRect clip_rect = window->ClipRect; ImVec2 text_size(0,0); - if (text_pos.y <= clip_rect.Max.y) + // Lines to skip (can't skip when logging text) + ImVec2 pos = text_pos; + if (!g.LogEnabled) { - ImVec2 pos = text_pos; - - // Lines to skip (can't skip when logging text) - if (!g.LogEnabled) - { - int lines_skippable = (int)((clip_rect.Min.y - text_pos.y) / line_height); - if (lines_skippable > 0) - { - int lines_skipped = 0; - while (line < text_end && lines_skipped < lines_skippable) - { - const char* line_end = (const char*)memchr(line, '\n', text_end - line); - if (!line_end) - line_end = text_end; - line = line_end + 1; - lines_skipped++; - } - pos.y += lines_skipped * line_height; - } - } - - // Lines to render - if (line < text_end) + int lines_skippable = (int)((window->ClipRect.Min.y - text_pos.y) / line_height); + if (lines_skippable > 0) { - ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height)); - while (line < text_end) - { - if (IsClippedEx(line_rect, 0, false)) - break; - - const char* line_end = (const char*)memchr(line, '\n', text_end - line); - if (!line_end) - line_end = text_end; - const ImVec2 line_size = CalcTextSize(line, line_end, false); - text_size.x = ImMax(text_size.x, line_size.x); - RenderText(pos, line, line_end, false); - line = line_end + 1; - line_rect.Min.y += line_height; - line_rect.Max.y += line_height; - pos.y += line_height; - } - - // Count remaining lines int lines_skipped = 0; - while (line < text_end) + while (line < text_end && lines_skipped < lines_skippable) { const char* line_end = (const char*)memchr(line, '\n', text_end - line); if (!line_end) line_end = text_end; + if ((flags & ImGuiTextFlags_NoWidthForLargeClippedText) == 0) + text_size.x = ImMax(text_size.x, CalcTextSize(line, line_end).x); line = line_end + 1; lines_skipped++; } pos.y += lines_skipped * line_height; } + } + + // Lines to render + if (line < text_end) + { + ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height)); + while (line < text_end) + { + if (IsClippedEx(line_rect, 0, false)) + break; + + const char* line_end = (const char*)memchr(line, '\n', text_end - line); + if (!line_end) + line_end = text_end; + text_size.x = ImMax(text_size.x, CalcTextSize(line, line_end).x); + RenderText(pos, line, line_end, false); + line = line_end + 1; + line_rect.Min.y += line_height; + line_rect.Max.y += line_height; + pos.y += line_height; + } - text_size.y += (pos - text_pos).y; + // Count remaining lines + int lines_skipped = 0; + while (line < text_end) + { + const char* line_end = (const char*)memchr(line, '\n', text_end - line); + if (!line_end) + line_end = text_end; + if ((flags & ImGuiTextFlags_NoWidthForLargeClippedText) == 0) + text_size.x = ImMax(text_size.x, CalcTextSize(line, line_end).x); + line = line_end + 1; + lines_skipped++; + } + pos.y += lines_skipped * line_height; } + text_size.y = (pos - text_pos).y; ImRect bb(text_pos, text_pos + text_size); - ItemSize(bb); + ItemSize(text_size); ItemAdd(bb, 0); } else @@ -210,7 +225,6 @@ void ImGui::TextUnformatted(const char* text, const char* text_end) const float wrap_width = wrap_enabled ? CalcWrapWidthForPos(window->DC.CursorPos, wrap_pos_x) : 0.0f; const ImVec2 text_size = CalcTextSize(text_begin, text_end, false, wrap_width); - // Account of baseline offset ImRect bb(text_pos, text_pos + text_size); ItemSize(text_size); if (!ItemAdd(bb, 0)) @@ -221,6 +235,11 @@ void ImGui::TextUnformatted(const char* text, const char* text_end) } } +void ImGui::TextUnformatted(const char* text, const char* text_end) +{ + TextEx(text, text_end, ImGuiTextFlags_NoWidthForLargeClippedText); +} + void ImGui::Text(const char* fmt, ...) { va_list args; @@ -237,7 +256,7 @@ void ImGui::TextV(const char* fmt, va_list args) ImGuiContext& g = *GImGui; const char* text_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); - TextUnformatted(g.TempBuffer, text_end); + TextEx(g.TempBuffer, text_end, ImGuiTextFlags_NoWidthForLargeClippedText); } void ImGui::TextColored(const ImVec4& col, const char* fmt, ...) @@ -280,10 +299,12 @@ void ImGui::TextWrapped(const char* fmt, ...) void ImGui::TextWrappedV(const char* fmt, va_list args) { - bool need_wrap = (GImGui->CurrentWindow->DC.TextWrapPos < 0.0f); // Keep existing wrap position is one ia already set - if (need_wrap) PushTextWrapPos(0.0f); + bool need_backup = (GImGui->CurrentWindow->DC.TextWrapPos < 0.0f); // Keep existing wrap position if one is already set + if (need_backup) + PushTextWrapPos(0.0f); TextV(fmt, args); - if (need_wrap) PopTextWrapPos(); + if (need_backup) + PopTextWrapPos(); } void ImGui::LabelText(const char* label, const char* fmt, ...) @@ -394,6 +415,11 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool if ((flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredRootWindow == window) g.HoveredWindow = window; +#ifdef IMGUI_ENABLE_TEST_ENGINE + if (id != 0 && window->DC.LastItemId != id) + ImGuiTestEngineHook_ItemAdd(&g, bb, id); +#endif + bool pressed = false; bool hovered = ItemHoverable(bb, id); @@ -481,7 +507,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool // Set active id so it can be queried by user via IsItemActive(), equivalent of holding the mouse button. g.NavActivateId = id; // This is so SetActiveId assign a Nav source SetActiveID(id, window); - if (!(flags & ImGuiButtonFlags_NoNavFocus)) + if ((nav_activated_by_code || nav_activated_by_inputs) && !(flags & ImGuiButtonFlags_NoNavFocus)) SetFocusID(id, window); g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right) | (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); } @@ -490,6 +516,8 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool bool held = false; if (g.ActiveId == id) { + if (pressed) + g.ActiveIdHasBeenPressed = true; if (g.ActiveIdSource == ImGuiInputSource_Mouse) { if (g.ActiveIdIsJustActivated) @@ -539,7 +567,7 @@ bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags ImVec2 size = CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f); const ImRect bb(pos, pos + size); - ItemSize(bb, style.FramePadding.y); + ItemSize(size, style.FramePadding.y); if (!ItemAdd(bb, id)) return false; @@ -560,6 +588,7 @@ bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags //if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup)) // CloseCurrentPopup(); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.LastItemStatusFlags); return pressed; } @@ -593,7 +622,7 @@ bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg) const ImGuiID id = window->GetID(str_id); ImVec2 size = CalcItemSize(size_arg, 0.0f, 0.0f); const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); - ItemSize(bb); + ItemSize(size); if (!ItemAdd(bb, id)) return false; @@ -657,7 +686,7 @@ bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos, float radius) // Render ImVec2 center = bb.GetCenter(); if (hovered) - window->DrawList->AddCircleFilled(center, ImMax(2.0f, radius), GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : ImGuiCol_ButtonHovered), 9); + window->DrawList->AddCircleFilled(center, ImMax(2.0f, radius), GetColorU32(held ? ImGuiCol_ButtonActive : ImGuiCol_ButtonHovered), 9); float cross_extent = (radius * 0.7071f) - 1.0f; ImU32 cross_col = GetColorU32(ImGuiCol_Text); @@ -690,19 +719,25 @@ bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos) return pressed; } +ImGuiID ImGui::GetScrollbarID(ImGuiWindow* window, ImGuiAxis axis) +{ + return window->GetIDNoKeepAlive(axis == ImGuiAxis_X ? "#SCROLLX" : "#SCROLLY"); +} + // Vertical/Horizontal scrollbar // The entire piece of code below is rather confusing because: // - We handle absolute seeking (when first clicking outside the grab) and relative manipulation (afterward or when clicking inside the grab) // - We store values as normalized ratio and in a form that allows the window content to change while we are holding on a scrollbar // - We handle both horizontal and vertical scrollbars, which makes the terminology not ideal. -void ImGui::Scrollbar(ImGuiLayoutType direction) +void ImGui::Scrollbar(ImGuiAxis axis) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - const bool horizontal = (direction == ImGuiLayoutType_Horizontal); + const bool horizontal = (axis == ImGuiAxis_X); const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(horizontal ? "#SCROLLX" : "#SCROLLY"); + const ImGuiID id = GetScrollbarID(window, axis); + KeepAliveID(id); // Render background bool other_scrollbar = (horizontal ? window->ScrollbarY : window->ScrollbarX); @@ -714,9 +749,21 @@ void ImGui::Scrollbar(ImGuiLayoutType direction) : ImRect(window_rect.Max.x - style.ScrollbarSize, window->Pos.y + border_size, window_rect.Max.x - border_size, window_rect.Max.y - other_scrollbar_size_w - border_size); if (!horizontal) bb.Min.y += window->TitleBarHeight() + ((window->Flags & ImGuiWindowFlags_MenuBar) ? window->MenuBarHeight() : 0.0f); - if (bb.GetWidth() <= 0.0f || bb.GetHeight() <= 0.0f) + + const float bb_height = bb.GetHeight(); + if (bb.GetWidth() <= 0.0f || bb_height <= 0.0f) return; + // When we are too small, start hiding and disabling the grab (this reduce visual noise on very small window and facilitate using the resize grab) + float alpha = 1.0f; + if ((axis == ImGuiAxis_Y) && bb_height < g.FontSize + g.Style.FramePadding.y * 2.0f) + { + alpha = ImSaturate((bb_height - g.FontSize) / (g.Style.FramePadding.y * 2.0f)); + if (alpha <= 0.0f) + return; + } + const bool allow_interaction = (alpha >= 1.0f); + int window_rounding_corners; if (horizontal) window_rounding_corners = ImDrawCornerFlags_BotLeft | (other_scrollbar ? 0 : ImDrawCornerFlags_BotRight); @@ -747,7 +794,7 @@ void ImGui::Scrollbar(ImGuiLayoutType direction) float scroll_max = ImMax(1.0f, win_size_contents_v - win_size_avail_v); float scroll_ratio = ImSaturate(scroll_v / scroll_max); float grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v; - if (held && grab_h_norm < 1.0f) + if (held && allow_interaction && grab_h_norm < 1.0f) { float scrollbar_pos_v = horizontal ? bb.Min.x : bb.Min.y; float mouse_pos_v = horizontal ? g.IO.MousePos.x : g.IO.MousePos.y; @@ -790,8 +837,8 @@ void ImGui::Scrollbar(ImGuiLayoutType direction) *click_delta_to_grab_center_v = clicked_v_norm - grab_v_norm - grab_h_norm*0.5f; } - // Render - const ImU32 grab_col = GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab); + // Render grab + const ImU32 grab_col = GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab, alpha); ImRect grab_rect; if (horizontal) grab_rect = ImRect(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm), bb.Min.y, ImMin(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm) + grab_h_pixels, window_rect.Max.x), bb.Max.y); @@ -875,19 +922,10 @@ bool ImGui::Checkbox(const char* label, bool* v) const ImGuiID id = window->GetID(label); const ImVec2 label_size = CalcTextSize(label, NULL, true); - const ImRect check_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(label_size.y + style.FramePadding.y*2, label_size.y + style.FramePadding.y*2)); // We want a square shape to we use Y twice - ItemSize(check_bb, style.FramePadding.y); - - ImRect total_bb = check_bb; - if (label_size.x > 0) - SameLine(0, style.ItemInnerSpacing.x); - const ImRect text_bb(window->DC.CursorPos + ImVec2(0,style.FramePadding.y), window->DC.CursorPos + ImVec2(0,style.FramePadding.y) + label_size); - if (label_size.x > 0) - { - ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight()), style.FramePadding.y); - total_bb = ImRect(ImMin(check_bb.Min, text_bb.Min), ImMax(check_bb.Max, text_bb.Max)); - } - + const float square_sz = GetFrameHeight(); + const ImVec2 pos = window->DC.CursorPos; + const ImRect total_bb(pos, pos + ImVec2(square_sz + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), label_size.y + style.FramePadding.y * 2.0f)); + ItemSize(total_bb, style.FramePadding.y); if (!ItemAdd(total_bb, id)) return false; @@ -899,20 +937,21 @@ bool ImGui::Checkbox(const char* label, bool* v) MarkItemEdited(id); } + const ImRect check_bb(pos, pos + ImVec2(square_sz, square_sz)); RenderNavHighlight(total_bb, id); RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding); if (*v) { - const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight()); - const float pad = ImMax(1.0f, (float)(int)(check_sz / 6.0f)); - RenderCheckMark(check_bb.Min + ImVec2(pad,pad), GetColorU32(ImGuiCol_CheckMark), check_bb.GetWidth() - pad*2.0f); + const float pad = ImMax(1.0f, (float)(int)(square_sz / 6.0f)); + RenderCheckMark(check_bb.Min + ImVec2(pad, pad), GetColorU32(ImGuiCol_CheckMark), square_sz - pad*2.0f); } if (g.LogEnabled) - LogRenderedText(&text_bb.Min, *v ? "[x]" : "[ ]"); + LogRenderedText(&total_bb.Min, *v ? "[x]" : "[ ]"); if (label_size.x > 0.0f) - RenderText(text_bb.Min, label); + RenderText(ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y), label); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0)); return pressed; } @@ -942,26 +981,18 @@ bool ImGui::RadioButton(const char* label, bool active) const ImGuiID id = window->GetID(label); const ImVec2 label_size = CalcTextSize(label, NULL, true); - const ImRect check_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(label_size.y + style.FramePadding.y*2-1, label_size.y + style.FramePadding.y*2-1)); - ItemSize(check_bb, style.FramePadding.y); - - ImRect total_bb = check_bb; - if (label_size.x > 0) - SameLine(0, style.ItemInnerSpacing.x); - const ImRect text_bb(window->DC.CursorPos + ImVec2(0, style.FramePadding.y), window->DC.CursorPos + ImVec2(0, style.FramePadding.y) + label_size); - if (label_size.x > 0) - { - ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight()), style.FramePadding.y); - total_bb.Add(text_bb); - } - + const float square_sz = GetFrameHeight(); + const ImVec2 pos = window->DC.CursorPos; + const ImRect check_bb(pos, pos + ImVec2(square_sz, square_sz)); + const ImRect total_bb(pos, pos + ImVec2(square_sz + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), label_size.y + style.FramePadding.y * 2.0f)); + ItemSize(total_bb, style.FramePadding.y); if (!ItemAdd(total_bb, id)) return false; ImVec2 center = check_bb.GetCenter(); center.x = (float)(int)center.x + 0.5f; center.y = (float)(int)center.y + 0.5f; - const float radius = check_bb.GetHeight() * 0.5f; + const float radius = (square_sz - 1.0f) * 0.5f; bool hovered, held; bool pressed = ButtonBehavior(total_bb, id, &hovered, &held); @@ -972,21 +1003,20 @@ bool ImGui::RadioButton(const char* label, bool active) window->DrawList->AddCircleFilled(center, radius, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), 16); if (active) { - const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight()); - const float pad = ImMax(1.0f, (float)(int)(check_sz / 6.0f)); - window->DrawList->AddCircleFilled(center, radius-pad, GetColorU32(ImGuiCol_CheckMark), 16); + const float pad = ImMax(1.0f, (float)(int)(square_sz / 6.0f)); + window->DrawList->AddCircleFilled(center, radius - pad, GetColorU32(ImGuiCol_CheckMark), 16); } if (style.FrameBorderSize > 0.0f) { - window->DrawList->AddCircle(center+ImVec2(1,1), radius, GetColorU32(ImGuiCol_BorderShadow), 16, style.FrameBorderSize); + window->DrawList->AddCircle(center + ImVec2(1,1), radius, GetColorU32(ImGuiCol_BorderShadow), 16, style.FrameBorderSize); window->DrawList->AddCircle(center, radius, GetColorU32(ImGuiCol_Border), 16, style.FrameBorderSize); } if (g.LogEnabled) - LogRenderedText(&text_bb.Min, active ? "(x)" : "( )"); + LogRenderedText(&total_bb.Min, active ? "(x)" : "( )"); if (label_size.x > 0.0f) - RenderText(text_bb.Min, label); + RenderText(ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y), label); return pressed; } @@ -1125,7 +1155,7 @@ void ImGui::Separator() // Those flags should eventually be overridable by the user ImGuiSeparatorFlags flags = (window->DC.LayoutType == ImGuiLayoutType_Horizontal) ? ImGuiSeparatorFlags_Vertical : ImGuiSeparatorFlags_Horizontal; - IM_ASSERT(ImIsPowerOfTwo((int)(flags & (ImGuiSeparatorFlags_Horizontal | ImGuiSeparatorFlags_Vertical)))); // Check that only 1 option is selected + IM_ASSERT(ImIsPowerOfTwo(flags & (ImGuiSeparatorFlags_Horizontal | ImGuiSeparatorFlags_Vertical))); // Check that only 1 option is selected if (flags & ImGuiSeparatorFlags_Vertical) { VerticalSeparator(); @@ -1153,7 +1183,7 @@ void ImGui::Separator() window->DrawList->AddLine(bb.Min, ImVec2(bb.Max.x,bb.Min.y), GetColorU32(ImGuiCol_Separator)); if (g.LogEnabled) - LogRenderedText(NULL, IM_NEWLINE "--------------------------------"); + LogRenderedText(&bb.Min, "--------------------------------"); if (window->DC.ColumnsSet) { @@ -1239,7 +1269,6 @@ bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float return held; } - //------------------------------------------------------------------------- // [SECTION] Widgets: ComboBox //------------------------------------------------------------------------- @@ -1330,7 +1359,7 @@ bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboF } char name[16]; - ImFormatString(name, IM_ARRAYSIZE(name), "##Combo_%02d", g.CurrentPopupStack.Size); // Recycle windows based on depth + ImFormatString(name, IM_ARRAYSIZE(name), "##Combo_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth // Peak into expected window size so we can position it if (ImGuiWindow* popup_window = FindWindowByName(name)) @@ -1441,7 +1470,7 @@ bool ImGui::Combo(const char* label, int* current_item, const char* const items[ return value_changed; } -// Combo box helper allowing to pass all items in a single string literal holding multiple zero-terminated items "item1\0item2\0" +// Combo box helper allowing to pass all items in a single string literal holding multiple zero-terminated items "item1\0item2\0" bool ImGui::Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int height_in_items) { int items_count = 0; @@ -1475,17 +1504,21 @@ struct ImGuiDataTypeInfo static const ImGuiDataTypeInfo GDataTypeInfo[] = { - { sizeof(int), "%d", "%d" }, - { sizeof(unsigned int), "%u", "%u" }, + { sizeof(char), "%d", "%d" }, // ImGuiDataType_S8 + { sizeof(unsigned char), "%u", "%u" }, + { sizeof(short), "%d", "%d" }, // ImGuiDataType_S16 + { sizeof(unsigned short), "%u", "%u" }, + { sizeof(int), "%d", "%d" }, // ImGuiDataType_S32 + { sizeof(unsigned int), "%u", "%u" }, #ifdef _MSC_VER - { sizeof(ImS64), "%I64d","%I64d" }, - { sizeof(ImU64), "%I64u","%I64u" }, + { sizeof(ImS64), "%I64d","%I64d" }, // ImGuiDataType_S64 + { sizeof(ImU64), "%I64u","%I64u" }, #else - { sizeof(ImS64), "%lld", "%lld" }, - { sizeof(ImU64), "%llu", "%llu" }, + { sizeof(ImS64), "%lld", "%lld" }, // ImGuiDataType_S64 + { sizeof(ImU64), "%llu", "%llu" }, #endif - { sizeof(float), "%f", "%f" }, // float are promoted to double in va_arg - { sizeof(double), "%f", "%lf" }, + { sizeof(float), "%f", "%f" }, // ImGuiDataType_Float (float are promoted to double in va_arg) + { sizeof(double), "%f", "%lf" }, // ImGuiDataType_Double }; IM_STATIC_ASSERT(IM_ARRAYSIZE(GDataTypeInfo) == ImGuiDataType_COUNT); @@ -1515,47 +1548,71 @@ static const char* PatchFormatStringFloatToInt(const char* fmt) static inline int DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* data_ptr, const char* format) { - if (data_type == ImGuiDataType_S32 || data_type == ImGuiDataType_U32) // Signedness doesn't matter when pushing the argument + // Signedness doesn't matter when pushing integer arguments + if (data_type == ImGuiDataType_S32 || data_type == ImGuiDataType_U32) return ImFormatString(buf, buf_size, format, *(const ImU32*)data_ptr); - if (data_type == ImGuiDataType_S64 || data_type == ImGuiDataType_U64) // Signedness doesn't matter when pushing the argument + if (data_type == ImGuiDataType_S64 || data_type == ImGuiDataType_U64) return ImFormatString(buf, buf_size, format, *(const ImU64*)data_ptr); if (data_type == ImGuiDataType_Float) return ImFormatString(buf, buf_size, format, *(const float*)data_ptr); if (data_type == ImGuiDataType_Double) return ImFormatString(buf, buf_size, format, *(const double*)data_ptr); + if (data_type == ImGuiDataType_S8) + return ImFormatString(buf, buf_size, format, *(const ImS8*)data_ptr); + if (data_type == ImGuiDataType_U8) + return ImFormatString(buf, buf_size, format, *(const ImU8*)data_ptr); + if (data_type == ImGuiDataType_S16) + return ImFormatString(buf, buf_size, format, *(const ImS16*)data_ptr); + if (data_type == ImGuiDataType_U16) + return ImFormatString(buf, buf_size, format, *(const ImU16*)data_ptr); IM_ASSERT(0); return 0; } -// FIXME: Adding support for clamping on boundaries of the data type would be nice. static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, void* arg1, const void* arg2) { IM_ASSERT(op == '+' || op == '-'); switch (data_type) { + case ImGuiDataType_S8: + if (op == '+') { *(ImS8*)output = ImAddClampOverflow(*(const ImS8*)arg1, *(const ImS8*)arg2, IM_S8_MIN, IM_S8_MAX); } + if (op == '-') { *(ImS8*)output = ImSubClampOverflow(*(const ImS8*)arg1, *(const ImS8*)arg2, IM_S8_MIN, IM_S8_MAX); } + return; + case ImGuiDataType_U8: + if (op == '+') { *(ImU8*)output = ImAddClampOverflow(*(const ImU8*)arg1, *(const ImU8*)arg2, IM_U8_MIN, IM_U8_MAX); } + if (op == '-') { *(ImU8*)output = ImSubClampOverflow(*(const ImU8*)arg1, *(const ImU8*)arg2, IM_U8_MIN, IM_U8_MAX); } + return; + case ImGuiDataType_S16: + if (op == '+') { *(ImS16*)output = ImAddClampOverflow(*(const ImS16*)arg1, *(const ImS16*)arg2, IM_S16_MIN, IM_S16_MAX); } + if (op == '-') { *(ImS16*)output = ImSubClampOverflow(*(const ImS16*)arg1, *(const ImS16*)arg2, IM_S16_MIN, IM_S16_MAX); } + return; + case ImGuiDataType_U16: + if (op == '+') { *(ImU16*)output = ImAddClampOverflow(*(const ImU16*)arg1, *(const ImU16*)arg2, IM_U16_MIN, IM_U16_MAX); } + if (op == '-') { *(ImU16*)output = ImSubClampOverflow(*(const ImU16*)arg1, *(const ImU16*)arg2, IM_U16_MIN, IM_U16_MAX); } + return; case ImGuiDataType_S32: - if (op == '+') *(int*)output = *(const int*)arg1 + *(const int*)arg2; - else if (op == '-') *(int*)output = *(const int*)arg1 - *(const int*)arg2; + if (op == '+') { *(ImS32*)output = ImAddClampOverflow(*(const ImS32*)arg1, *(const ImS32*)arg2, IM_S32_MIN, IM_S32_MAX); } + if (op == '-') { *(ImS32*)output = ImSubClampOverflow(*(const ImS32*)arg1, *(const ImS32*)arg2, IM_S32_MIN, IM_S32_MAX); } return; case ImGuiDataType_U32: - if (op == '+') *(unsigned int*)output = *(const unsigned int*)arg1 + *(const ImU32*)arg2; - else if (op == '-') *(unsigned int*)output = *(const unsigned int*)arg1 - *(const ImU32*)arg2; + if (op == '+') { *(ImU32*)output = ImAddClampOverflow(*(const ImU32*)arg1, *(const ImU32*)arg2, IM_U32_MIN, IM_U32_MAX); } + if (op == '-') { *(ImU32*)output = ImSubClampOverflow(*(const ImU32*)arg1, *(const ImU32*)arg2, IM_U32_MIN, IM_U32_MAX); } return; case ImGuiDataType_S64: - if (op == '+') *(ImS64*)output = *(const ImS64*)arg1 + *(const ImS64*)arg2; - else if (op == '-') *(ImS64*)output = *(const ImS64*)arg1 - *(const ImS64*)arg2; + if (op == '+') { *(ImS64*)output = ImAddClampOverflow(*(const ImS64*)arg1, *(const ImS64*)arg2, IM_S64_MIN, IM_S64_MAX); } + if (op == '-') { *(ImS64*)output = ImSubClampOverflow(*(const ImS64*)arg1, *(const ImS64*)arg2, IM_S64_MIN, IM_S64_MAX); } return; case ImGuiDataType_U64: - if (op == '+') *(ImU64*)output = *(const ImU64*)arg1 + *(const ImU64*)arg2; - else if (op == '-') *(ImU64*)output = *(const ImU64*)arg1 - *(const ImU64*)arg2; + if (op == '+') { *(ImU64*)output = ImAddClampOverflow(*(const ImU64*)arg1, *(const ImU64*)arg2, IM_U64_MIN, IM_U64_MAX); } + if (op == '-') { *(ImU64*)output = ImSubClampOverflow(*(const ImU64*)arg1, *(const ImU64*)arg2, IM_U64_MIN, IM_U64_MAX); } return; case ImGuiDataType_Float: - if (op == '+') *(float*)output = *(const float*)arg1 + *(const float*)arg2; - else if (op == '-') *(float*)output = *(const float*)arg1 - *(const float*)arg2; + if (op == '+') { *(float*)output = *(const float*)arg1 + *(const float*)arg2; } + if (op == '-') { *(float*)output = *(const float*)arg1 - *(const float*)arg2; } return; case ImGuiDataType_Double: - if (op == '+') *(double*)output = *(const double*)arg1 + *(const double*)arg2; - else if (op == '-') *(double*)output = *(const double*)arg1 - *(const double*)arg2; + if (op == '+') { *(double*)output = *(const double*)arg1 + *(const double*)arg2; } + if (op == '-') { *(double*)output = *(const double*)arg1 - *(const double*)arg2; } return; case ImGuiDataType_COUNT: break; } @@ -1594,6 +1651,7 @@ static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_b if (format == NULL) format = GDataTypeInfo[data_type].ScanFmt; + // FIXME-LEGACY: The aim is to remove those operators and write a proper expression evaluator at some point.. int arg1i = 0; if (data_type == ImGuiDataType_S32) { @@ -1608,12 +1666,6 @@ static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_b else if (op == '/') { if (sscanf(buf, "%f", &arg1f) && arg1f != 0.0f) *v = (int)(arg0i / arg1f); } // Divide else { if (sscanf(buf, format, &arg1i) == 1) *v = arg1i; } // Assign constant } - else if (data_type == ImGuiDataType_U32 || data_type == ImGuiDataType_S64 || data_type == ImGuiDataType_U64) - { - // Assign constant - // FIXME: We don't bother handling support for legacy operators since they are a little too crappy. Instead we may implement a proper expression evaluator in the future. - sscanf(buf, format, data_ptr); - } else if (data_type == ImGuiDataType_Float) { // For floats we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in @@ -1643,6 +1695,29 @@ static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_b else if (op == '/') { if (arg1f != 0.0f) *v = arg0f / arg1f; } // Divide else { *v = arg1f; } // Assign constant } + else if (data_type == ImGuiDataType_U32 || data_type == ImGuiDataType_S64 || data_type == ImGuiDataType_U64) + { + // All other types assign constant + // We don't bother handling support for legacy operators since they are a little too crappy. Instead we will later implement a proper expression evaluator in the future. + sscanf(buf, format, data_ptr); + } + else + { + // Small types need a 32-bit buffer to receive the result from scanf() + int v32; + sscanf(buf, format, &v32); + if (data_type == ImGuiDataType_S8) + *(ImS8*)data_ptr = (ImS8)ImClamp(v32, (int)IM_S8_MIN, (int)IM_S8_MAX); + else if (data_type == ImGuiDataType_U8) + *(ImU8*)data_ptr = (ImU8)ImClamp(v32, (int)IM_U8_MIN, (int)IM_U8_MAX); + else if (data_type == ImGuiDataType_S16) + *(ImS16*)data_ptr = (ImS16)ImClamp(v32, (int)IM_S16_MIN, (int)IM_S16_MAX); + else if (data_type == ImGuiDataType_U16) + *(ImU16*)data_ptr = (ImU16)ImClamp(v32, (int)IM_U16_MIN, (int)IM_U16_MAX); + else + IM_ASSERT(0); + } + return memcmp(data_backup, data_ptr, GDataTypeInfo[data_type].Size) != 0; } @@ -1651,7 +1726,7 @@ static float GetMinimumStepAtDecimalPrecision(int decimal_precision) static const float min_steps[10] = { 1.0f, 0.1f, 0.01f, 0.001f, 0.0001f, 0.00001f, 0.000001f, 0.0000001f, 0.00000001f, 0.000000001f }; if (decimal_precision < 0) return FLT_MIN; - return (decimal_precision >= 0 && decimal_precision < 10) ? min_steps[decimal_precision] : ImPow(10.0f, (float)-decimal_precision); + return (decimal_precision < IM_ARRAYSIZE(min_steps)) ? min_steps[decimal_precision] : ImPow(10.0f, (float)-decimal_precision); } template @@ -1712,6 +1787,7 @@ bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const const ImGuiAxis axis = (flags & ImGuiDragFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X; const bool is_decimal = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double); const bool has_min_max = (v_min != v_max); + const bool is_power = (power != 1.0f && is_decimal && has_min_max && (v_max - v_min < FLT_MAX)); // Default tweak speed if (v_speed == 0.0f && has_min_max && (v_max - v_min < FLT_MAX)) @@ -1743,7 +1819,8 @@ bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const // Avoid altering values and clamping when we are _already_ past the limits and heading in the same direction, so e.g. if range is 0..255, current value is 300 and we are pushing to the right side, keep the 300. bool is_just_activated = g.ActiveIdIsJustActivated; bool is_already_past_limits_and_pushing_outward = has_min_max && ((*v >= v_max && adjust_delta > 0.0f) || (*v <= v_min && adjust_delta < 0.0f)); - if (is_just_activated || is_already_past_limits_and_pushing_outward) + bool is_drag_direction_change_with_power = is_power && ((adjust_delta < 0 && g.DragCurrentAccum > 0) || (adjust_delta > 0 && g.DragCurrentAccum < 0)); + if (is_just_activated || is_already_past_limits_and_pushing_outward || is_drag_direction_change_with_power) { g.DragCurrentAccum = 0.0f; g.DragCurrentAccumDirty = false; @@ -1760,7 +1837,6 @@ bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const TYPE v_cur = *v; FLOATTYPE v_old_ref_for_accum_remainder = (FLOATTYPE)0.0f; - const bool is_power = (power != 1.0f && is_decimal && has_min_max && (v_max - v_min < FLT_MAX)); if (is_power) { // Offset + round to user desired precision, with a curve on the v_min..v_max range to get more precision on one side of the range @@ -1824,6 +1900,10 @@ bool ImGui::DragBehavior(ImGuiID id, ImGuiDataType data_type, void* v, float v_s switch (data_type) { + case ImGuiDataType_S8: { ImS32 v32 = (ImS32)*(ImS8*)v; bool r = DragBehaviorT(ImGuiDataType_S32, &v32, v_speed, v_min ? *(const ImS8*) v_min : IM_S8_MIN, v_max ? *(const ImS8*)v_max : IM_S8_MAX, format, power, flags); if (r) *(ImS8*)v = (ImS8)v32; return r; } + case ImGuiDataType_U8: { ImU32 v32 = (ImU32)*(ImU8*)v; bool r = DragBehaviorT(ImGuiDataType_U32, &v32, v_speed, v_min ? *(const ImU8*) v_min : IM_U8_MIN, v_max ? *(const ImU8*)v_max : IM_U8_MAX, format, power, flags); if (r) *(ImU8*)v = (ImU8)v32; return r; } + case ImGuiDataType_S16: { ImS32 v32 = (ImS32)*(ImS16*)v; bool r = DragBehaviorT(ImGuiDataType_S32, &v32, v_speed, v_min ? *(const ImS16*)v_min : IM_S16_MIN, v_max ? *(const ImS16*)v_max : IM_S16_MAX, format, power, flags); if (r) *(ImS16*)v = (ImS16)v32; return r; } + case ImGuiDataType_U16: { ImU32 v32 = (ImU32)*(ImU16*)v; bool r = DragBehaviorT(ImGuiDataType_U32, &v32, v_speed, v_min ? *(const ImU16*)v_min : IM_U16_MIN, v_max ? *(const ImU16*)v_max : IM_U16_MAX, format, power, flags); if (r) *(ImU16*)v = (ImU16)v32; return r; } case ImGuiDataType_S32: return DragBehaviorT(data_type, (ImS32*)v, v_speed, v_min ? *(const ImS32* )v_min : IM_S32_MIN, v_max ? *(const ImS32* )v_max : IM_S32_MAX, format, power, flags); case ImGuiDataType_U32: return DragBehaviorT(data_type, (ImU32*)v, v_speed, v_min ? *(const ImU32* )v_min : IM_U32_MIN, v_max ? *(const ImU32* )v_max : IM_U32_MAX, format, power, flags); case ImGuiDataType_S64: return DragBehaviorT(data_type, (ImS64*)v, v_speed, v_min ? *(const ImS64* )v_min : IM_S64_MIN, v_max ? *(const ImS64* )v_max : IM_S64_MAX, format, power, flags); @@ -1852,15 +1932,12 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* v, floa const ImVec2 label_size = CalcTextSize(label, NULL, true); const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f)); - const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding); const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); - // NB- we don't call ItemSize() yet because we may turn into a text edit box below + ItemSize(total_bb, style.FramePadding.y); if (!ItemAdd(total_bb, id, &frame_bb)) - { - ItemSize(total_bb, style.FramePadding.y); return false; - } + const bool hovered = ItemHoverable(frame_bb, id); // Default format string when passing NULL @@ -1873,14 +1950,14 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* v, floa // Tabbing or CTRL-clicking on Drag turns it into an input box bool start_text_input = false; - const bool tab_focus_requested = FocusableItemRegister(window, id); - if (tab_focus_requested || (hovered && (g.IO.MouseClicked[0] || g.IO.MouseDoubleClicked[0])) || g.NavActivateId == id || (g.NavInputId == id && g.ScalarAsInputTextId != id)) + const bool focus_requested = FocusableItemRegister(window, id); + if (focus_requested || (hovered && (g.IO.MouseClicked[0] || g.IO.MouseDoubleClicked[0])) || g.NavActivateId == id || (g.NavInputId == id && g.ScalarAsInputTextId != id)) { SetActiveID(id, window); SetFocusID(id, window); FocusWindow(window); g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); - if (tab_focus_requested || g.IO.KeyCtrl || g.IO.MouseDoubleClicked[0] || g.NavInputId == id) + if (focus_requested || g.IO.KeyCtrl || g.IO.MouseDoubleClicked[0] || g.NavInputId == id) { start_text_input = true; g.ScalarAsInputTextId = 0; @@ -1888,12 +1965,12 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* v, floa } if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id)) { + window->DC.CursorPos = frame_bb.Min; FocusableItemUnregister(window); return InputScalarAsWidgetReplacement(frame_bb, id, label, data_type, v, format); } // Actual drag behavior - ItemSize(total_bb, style.FramePadding.y); const bool value_changed = DragBehavior(id, data_type, v, v_speed, v_min, v_max, format, power, ImGuiDragFlags_None); if (value_changed) MarkItemEdited(id); @@ -1909,8 +1986,9 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* v, floa RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.5f)); if (label_size.x > 0.0f) - RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label); + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags); return value_changed; } @@ -1929,7 +2007,7 @@ bool ImGui::DragScalarN(const char* label, ImGuiDataType data_type, void* v, int for (int i = 0; i < components; i++) { PushID(i); - value_changed |= DragScalar("##v", data_type, v, v_speed, v_min, v_max, format, power); + value_changed |= DragScalar("", data_type, v, v_speed, v_min, v_max, format, power); SameLine(0, g.Style.ItemInnerSpacing.x); PopID(); PopItemWidth(); @@ -1937,7 +2015,7 @@ bool ImGui::DragScalarN(const char* label, ImGuiDataType data_type, void* v, int } PopID(); - TextUnformatted(label, FindRenderedTextEnd(label)); + TextEx(label, FindRenderedTextEnd(label)); EndGroup(); return value_changed; } @@ -1980,7 +2058,7 @@ bool ImGui::DragFloatRange2(const char* label, float* v_current_min, float* v_cu PopItemWidth(); SameLine(0, g.Style.ItemInnerSpacing.x); - TextUnformatted(label, FindRenderedTextEnd(label)); + TextEx(label, FindRenderedTextEnd(label)); EndGroup(); PopID(); return value_changed; @@ -2025,7 +2103,7 @@ bool ImGui::DragIntRange2(const char* label, int* v_current_min, int* v_current_ PopItemWidth(); SameLine(0, g.Style.ItemInnerSpacing.x); - TextUnformatted(label, FindRenderedTextEnd(label)); + TextEx(label, FindRenderedTextEnd(label)); EndGroup(); PopID(); @@ -2250,6 +2328,10 @@ bool ImGui::SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type { switch (data_type) { + case ImGuiDataType_S8: { ImS32 v32 = (ImS32)*(ImS8*)v; bool r = SliderBehaviorT(bb, id, ImGuiDataType_S32, &v32, *(const ImS8*)v_min, *(const ImS8*)v_max, format, power, flags, out_grab_bb); if (r) *(ImS8*)v = (ImS8)v32; return r; } + case ImGuiDataType_U8: { ImU32 v32 = (ImU32)*(ImU8*)v; bool r = SliderBehaviorT(bb, id, ImGuiDataType_U32, &v32, *(const ImU8*)v_min, *(const ImU8*)v_max, format, power, flags, out_grab_bb); if (r) *(ImU8*)v = (ImU8)v32; return r; } + case ImGuiDataType_S16: { ImS32 v32 = (ImS32)*(ImS16*)v; bool r = SliderBehaviorT(bb, id, ImGuiDataType_S32, &v32, *(const ImS16*)v_min, *(const ImS16*)v_max, format, power, flags, out_grab_bb); if (r) *(ImS16*)v = (ImS16)v32; return r; } + case ImGuiDataType_U16: { ImU32 v32 = (ImU32)*(ImU16*)v; bool r = SliderBehaviorT(bb, id, ImGuiDataType_U32, &v32, *(const ImU16*)v_min, *(const ImU16*)v_max, format, power, flags, out_grab_bb); if (r) *(ImU16*)v = (ImU16)v32; return r; } case ImGuiDataType_S32: IM_ASSERT(*(const ImS32*)v_min >= IM_S32_MIN/2 && *(const ImS32*)v_max <= IM_S32_MAX/2); return SliderBehaviorT(bb, id, data_type, (ImS32*)v, *(const ImS32*)v_min, *(const ImS32*)v_max, format, power, flags, out_grab_bb); @@ -2289,12 +2371,9 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* v, co const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f)); const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); - // NB- we don't call ItemSize() yet because we may turn into a text edit box below + ItemSize(total_bb, style.FramePadding.y); if (!ItemAdd(total_bb, id, &frame_bb)) - { - ItemSize(total_bb, style.FramePadding.y); return false; - } // Default format string when passing NULL // Patch old "%.0f" format string to use "%d", read function comments for more details. @@ -2306,15 +2385,15 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* v, co // Tabbing or CTRL-clicking on Slider turns it into an input box bool start_text_input = false; - const bool tab_focus_requested = FocusableItemRegister(window, id); + const bool focus_requested = FocusableItemRegister(window, id); const bool hovered = ItemHoverable(frame_bb, id); - if (tab_focus_requested || (hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || (g.NavInputId == id && g.ScalarAsInputTextId != id)) + if (focus_requested || (hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || (g.NavInputId == id && g.ScalarAsInputTextId != id)) { SetActiveID(id, window); SetFocusID(id, window); FocusWindow(window); g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); - if (tab_focus_requested || g.IO.KeyCtrl || g.NavInputId == id) + if (focus_requested || g.IO.KeyCtrl || g.NavInputId == id) { start_text_input = true; g.ScalarAsInputTextId = 0; @@ -2322,12 +2401,11 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* v, co } if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id)) { + window->DC.CursorPos = frame_bb.Min; FocusableItemUnregister(window); return InputScalarAsWidgetReplacement(frame_bb, id, label, data_type, v, format); } - ItemSize(total_bb, style.FramePadding.y); - // Draw frame const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); RenderNavHighlight(frame_bb, id); @@ -2350,6 +2428,7 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* v, co if (label_size.x > 0.0f) RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags); return value_changed; } @@ -2369,7 +2448,7 @@ bool ImGui::SliderScalarN(const char* label, ImGuiDataType data_type, void* v, i for (int i = 0; i < components; i++) { PushID(i); - value_changed |= SliderScalar("##v", data_type, v, v_min, v_max, format, power); + value_changed |= SliderScalar("", data_type, v, v_min, v_max, format, power); SameLine(0, g.Style.ItemInnerSpacing.x); PopID(); PopItemWidth(); @@ -2377,7 +2456,7 @@ bool ImGui::SliderScalarN(const char* label, ImGuiDataType data_type, void* v, i } PopID(); - TextUnformatted(label, FindRenderedTextEnd(label)); + TextEx(label, FindRenderedTextEnd(label)); EndGroup(); return value_changed; } @@ -2559,7 +2638,7 @@ const char* ImParseFormatFindEnd(const char* fmt) // fmt = "%.3f" -> return fmt // fmt = "hello %.3f" -> return fmt + 6 // fmt = "%.3f hello" -> return buf written with "%.3f" -const char* ImParseFormatTrimDecorations(const char* fmt, char* buf, int buf_size) +const char* ImParseFormatTrimDecorations(const char* fmt, char* buf, size_t buf_size) { const char* fmt_start = ImParseFormatFindStart(fmt); if (fmt_start[0] != '%') @@ -2567,7 +2646,7 @@ const char* ImParseFormatTrimDecorations(const char* fmt, char* buf, int buf_siz const char* fmt_end = ImParseFormatFindEnd(fmt_start); if (fmt_end[0] == 0) // If we only have leading decoration, we don't need to copy the data. return fmt_start; - ImStrncpy(buf, fmt_start, ImMin((int)(fmt_end + 1 - fmt_start), buf_size)); + ImStrncpy(buf, fmt_start, ImMin((size_t)(fmt_end - fmt_start) + 1, buf_size)); return buf; } @@ -2596,17 +2675,16 @@ int ImParseFormatPrecision(const char* fmt, int default_precision) } // Create text input in place of an active drag/slider (used when doing a CTRL+Click on drag/slider widgets) -// FIXME: Logic is awkward and confusing. This should be reworked to facilitate using in other situations. +// FIXME: Facilitate using this in variety of other situations. bool ImGui::InputScalarAsWidgetReplacement(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* data_ptr, const char* format) { + IM_UNUSED(id); ImGuiContext& g = *GImGui; - ImGuiWindow* window = GetCurrentWindow(); - // Our replacement widget will override the focus ID (registered previously to allow for a TAB focus to happen) - // On the first frame, g.ScalarAsInputTextId == 0, then on subsequent frames it becomes == id - SetActiveID(g.ScalarAsInputTextId, window); - SetHoveredID(0); - g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); + // On the first frame, g.ScalarAsInputTextId == 0, then on subsequent frames it becomes == id. + // We clear ActiveID on the first frame to allow the InputText() taking it back. + if (g.ScalarAsInputTextId == 0) + ClearActiveID(); char fmt_buf[32]; char data_buf[32]; @@ -2614,27 +2692,26 @@ bool ImGui::InputScalarAsWidgetReplacement(const ImRect& bb, ImGuiID id, const c DataTypeFormatString(data_buf, IM_ARRAYSIZE(data_buf), data_type, data_ptr, format); ImStrTrimBlanks(data_buf); ImGuiInputTextFlags flags = ImGuiInputTextFlags_AutoSelectAll | ((data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) ? ImGuiInputTextFlags_CharsScientific : ImGuiInputTextFlags_CharsDecimal); - bool value_changed = InputTextEx(label, data_buf, IM_ARRAYSIZE(data_buf), bb.GetSize(), flags); - if (g.ScalarAsInputTextId == 0) // First frame we started displaying the InputText widget + bool value_changed = InputTextEx(label, NULL, data_buf, IM_ARRAYSIZE(data_buf), bb.GetSize(), flags); + if (g.ScalarAsInputTextId == 0) { - IM_ASSERT(g.ActiveId == id); // InputText ID expected to match the Slider ID + // First frame we started displaying the InputText widget, we expect it to take the active id. + IM_ASSERT(g.ActiveId == id); g.ScalarAsInputTextId = g.ActiveId; - SetHoveredID(id); } if (value_changed) - return DataTypeApplyOpFromText(data_buf, g.InputTextState.InitialText.Data, data_type, data_ptr, NULL); + return DataTypeApplyOpFromText(data_buf, g.InputTextState.InitialTextA.Data, data_type, data_ptr, NULL); return false; } -// NB: format here must be a simple "%xx" format string with no prefix/suffix (unlike the Drag/Slider functions "format" argument) -bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* data_ptr, const void* step, const void* step_fast, const char* format, ImGuiInputTextFlags extra_flags) +bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* data_ptr, const void* step, const void* step_fast, const char* format, ImGuiInputTextFlags flags) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; + ImGuiStyle& style = g.Style; IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT); if (format == NULL) @@ -2644,9 +2721,9 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* data_p DataTypeFormatString(buf, IM_ARRAYSIZE(buf), data_type, data_ptr, format); bool value_changed = false; - if ((extra_flags & (ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsScientific)) == 0) - extra_flags |= ImGuiInputTextFlags_CharsDecimal; - extra_flags |= ImGuiInputTextFlags_AutoSelectAll; + if ((flags & (ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsScientific)) == 0) + flags |= ImGuiInputTextFlags_CharsDecimal; + flags |= ImGuiInputTextFlags_AutoSelectAll; if (step != NULL) { @@ -2655,39 +2732,45 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* data_p BeginGroup(); // The only purpose of the group here is to allow the caller to query item data e.g. IsItemActive() PushID(label); PushItemWidth(ImMax(1.0f, CalcItemWidth() - (button_size + style.ItemInnerSpacing.x) * 2)); - if (InputText("", buf, IM_ARRAYSIZE(buf), extra_flags)) // PushId(label) + "" gives us the expected ID from outside point of view - value_changed = DataTypeApplyOpFromText(buf, g.InputTextState.InitialText.Data, data_type, data_ptr, format); + if (InputText("", buf, IM_ARRAYSIZE(buf), flags)) // PushId(label) + "" gives us the expected ID from outside point of view + value_changed = DataTypeApplyOpFromText(buf, g.InputTextState.InitialTextA.Data, data_type, data_ptr, format); PopItemWidth(); // Step buttons + const ImVec2 backup_frame_padding = style.FramePadding; + style.FramePadding.x = style.FramePadding.y; + ImGuiButtonFlags button_flags = ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups; + if (flags & ImGuiInputTextFlags_ReadOnly) + button_flags |= ImGuiButtonFlags_Disabled; SameLine(0, style.ItemInnerSpacing.x); - if (ButtonEx("-", ImVec2(button_size, button_size), ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups)) + if (ButtonEx("-", ImVec2(button_size, button_size), button_flags)) { DataTypeApplyOp(data_type, '-', data_ptr, data_ptr, g.IO.KeyCtrl && step_fast ? step_fast : step); value_changed = true; } SameLine(0, style.ItemInnerSpacing.x); - if (ButtonEx("+", ImVec2(button_size, button_size), ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups)) + if (ButtonEx("+", ImVec2(button_size, button_size), button_flags)) { DataTypeApplyOp(data_type, '+', data_ptr, data_ptr, g.IO.KeyCtrl && step_fast ? step_fast : step); value_changed = true; } SameLine(0, style.ItemInnerSpacing.x); - TextUnformatted(label, FindRenderedTextEnd(label)); + TextEx(label, FindRenderedTextEnd(label)); + style.FramePadding = backup_frame_padding; PopID(); EndGroup(); } else { - if (InputText(label, buf, IM_ARRAYSIZE(buf), extra_flags)) - value_changed = DataTypeApplyOpFromText(buf, g.InputTextState.InitialText.Data, data_type, data_ptr, format); + if (InputText(label, buf, IM_ARRAYSIZE(buf), flags)) + value_changed = DataTypeApplyOpFromText(buf, g.InputTextState.InitialTextA.Data, data_type, data_ptr, format); } return value_changed; } -bool ImGui::InputScalarN(const char* label, ImGuiDataType data_type, void* v, int components, const void* step, const void* step_fast, const char* format, ImGuiInputTextFlags extra_flags) +bool ImGui::InputScalarN(const char* label, ImGuiDataType data_type, void* v, int components, const void* step, const void* step_fast, const char* format, ImGuiInputTextFlags flags) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) @@ -2702,7 +2785,7 @@ bool ImGui::InputScalarN(const char* label, ImGuiDataType data_type, void* v, in for (int i = 0; i < components; i++) { PushID(i); - value_changed |= InputScalar("##v", data_type, v, step, step_fast, format, extra_flags); + value_changed |= InputScalar("", data_type, v, step, step_fast, format, flags); SameLine(0, g.Style.ItemInnerSpacing.x); PopID(); PopItemWidth(); @@ -2710,99 +2793,100 @@ bool ImGui::InputScalarN(const char* label, ImGuiDataType data_type, void* v, in } PopID(); - TextUnformatted(label, FindRenderedTextEnd(label)); + TextEx(label, FindRenderedTextEnd(label)); EndGroup(); return value_changed; } -bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, const char* format, ImGuiInputTextFlags extra_flags) +bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, const char* format, ImGuiInputTextFlags flags) { - extra_flags |= ImGuiInputTextFlags_CharsScientific; - return InputScalar(label, ImGuiDataType_Float, (void*)v, (void*)(step>0.0f ? &step : NULL), (void*)(step_fast>0.0f ? &step_fast : NULL), format, extra_flags); + flags |= ImGuiInputTextFlags_CharsScientific; + return InputScalar(label, ImGuiDataType_Float, (void*)v, (void*)(step>0.0f ? &step : NULL), (void*)(step_fast>0.0f ? &step_fast : NULL), format, flags); } -bool ImGui::InputFloat2(const char* label, float v[2], const char* format, ImGuiInputTextFlags extra_flags) +bool ImGui::InputFloat2(const char* label, float v[2], const char* format, ImGuiInputTextFlags flags) { - return InputScalarN(label, ImGuiDataType_Float, v, 2, NULL, NULL, format, extra_flags); + return InputScalarN(label, ImGuiDataType_Float, v, 2, NULL, NULL, format, flags); } -bool ImGui::InputFloat3(const char* label, float v[3], const char* format, ImGuiInputTextFlags extra_flags) +bool ImGui::InputFloat3(const char* label, float v[3], const char* format, ImGuiInputTextFlags flags) { - return InputScalarN(label, ImGuiDataType_Float, v, 3, NULL, NULL, format, extra_flags); + return InputScalarN(label, ImGuiDataType_Float, v, 3, NULL, NULL, format, flags); } -bool ImGui::InputFloat4(const char* label, float v[4], const char* format, ImGuiInputTextFlags extra_flags) +bool ImGui::InputFloat4(const char* label, float v[4], const char* format, ImGuiInputTextFlags flags) { - return InputScalarN(label, ImGuiDataType_Float, v, 4, NULL, NULL, format, extra_flags); + return InputScalarN(label, ImGuiDataType_Float, v, 4, NULL, NULL, format, flags); } // Prefer using "const char* format" directly, which is more flexible and consistent with other API. #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS -bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, int decimal_precision, ImGuiInputTextFlags extra_flags) +bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, int decimal_precision, ImGuiInputTextFlags flags) { char format[16] = "%f"; if (decimal_precision >= 0) ImFormatString(format, IM_ARRAYSIZE(format), "%%.%df", decimal_precision); - return InputFloat(label, v, step, step_fast, format, extra_flags); + return InputFloat(label, v, step, step_fast, format, flags); } -bool ImGui::InputFloat2(const char* label, float v[2], int decimal_precision, ImGuiInputTextFlags extra_flags) +bool ImGui::InputFloat2(const char* label, float v[2], int decimal_precision, ImGuiInputTextFlags flags) { char format[16] = "%f"; if (decimal_precision >= 0) ImFormatString(format, IM_ARRAYSIZE(format), "%%.%df", decimal_precision); - return InputScalarN(label, ImGuiDataType_Float, v, 2, NULL, NULL, format, extra_flags); + return InputScalarN(label, ImGuiDataType_Float, v, 2, NULL, NULL, format, flags); } -bool ImGui::InputFloat3(const char* label, float v[3], int decimal_precision, ImGuiInputTextFlags extra_flags) +bool ImGui::InputFloat3(const char* label, float v[3], int decimal_precision, ImGuiInputTextFlags flags) { char format[16] = "%f"; if (decimal_precision >= 0) ImFormatString(format, IM_ARRAYSIZE(format), "%%.%df", decimal_precision); - return InputScalarN(label, ImGuiDataType_Float, v, 3, NULL, NULL, format, extra_flags); + return InputScalarN(label, ImGuiDataType_Float, v, 3, NULL, NULL, format, flags); } -bool ImGui::InputFloat4(const char* label, float v[4], int decimal_precision, ImGuiInputTextFlags extra_flags) +bool ImGui::InputFloat4(const char* label, float v[4], int decimal_precision, ImGuiInputTextFlags flags) { char format[16] = "%f"; if (decimal_precision >= 0) ImFormatString(format, IM_ARRAYSIZE(format), "%%.%df", decimal_precision); - return InputScalarN(label, ImGuiDataType_Float, v, 4, NULL, NULL, format, extra_flags); + return InputScalarN(label, ImGuiDataType_Float, v, 4, NULL, NULL, format, flags); } #endif // IMGUI_DISABLE_OBSOLETE_FUNCTIONS -bool ImGui::InputInt(const char* label, int* v, int step, int step_fast, ImGuiInputTextFlags extra_flags) +bool ImGui::InputInt(const char* label, int* v, int step, int step_fast, ImGuiInputTextFlags flags) { // Hexadecimal input provided as a convenience but the flag name is awkward. Typically you'd use InputText() to parse your own data, if you want to handle prefixes. - const char* format = (extra_flags & ImGuiInputTextFlags_CharsHexadecimal) ? "%08X" : "%d"; - return InputScalar(label, ImGuiDataType_S32, (void*)v, (void*)(step>0 ? &step : NULL), (void*)(step_fast>0 ? &step_fast : NULL), format, extra_flags); + const char* format = (flags & ImGuiInputTextFlags_CharsHexadecimal) ? "%08X" : "%d"; + return InputScalar(label, ImGuiDataType_S32, (void*)v, (void*)(step>0 ? &step : NULL), (void*)(step_fast>0 ? &step_fast : NULL), format, flags); } -bool ImGui::InputInt2(const char* label, int v[2], ImGuiInputTextFlags extra_flags) +bool ImGui::InputInt2(const char* label, int v[2], ImGuiInputTextFlags flags) { - return InputScalarN(label, ImGuiDataType_S32, v, 2, NULL, NULL, "%d", extra_flags); + return InputScalarN(label, ImGuiDataType_S32, v, 2, NULL, NULL, "%d", flags); } -bool ImGui::InputInt3(const char* label, int v[3], ImGuiInputTextFlags extra_flags) +bool ImGui::InputInt3(const char* label, int v[3], ImGuiInputTextFlags flags) { - return InputScalarN(label, ImGuiDataType_S32, v, 3, NULL, NULL, "%d", extra_flags); + return InputScalarN(label, ImGuiDataType_S32, v, 3, NULL, NULL, "%d", flags); } -bool ImGui::InputInt4(const char* label, int v[4], ImGuiInputTextFlags extra_flags) +bool ImGui::InputInt4(const char* label, int v[4], ImGuiInputTextFlags flags) { - return InputScalarN(label, ImGuiDataType_S32, v, 4, NULL, NULL, "%d", extra_flags); + return InputScalarN(label, ImGuiDataType_S32, v, 4, NULL, NULL, "%d", flags); } -bool ImGui::InputDouble(const char* label, double* v, double step, double step_fast, const char* format, ImGuiInputTextFlags extra_flags) +bool ImGui::InputDouble(const char* label, double* v, double step, double step_fast, const char* format, ImGuiInputTextFlags flags) { - extra_flags |= ImGuiInputTextFlags_CharsScientific; - return InputScalar(label, ImGuiDataType_Double, (void*)v, (void*)(step>0.0 ? &step : NULL), (void*)(step_fast>0.0 ? &step_fast : NULL), format, extra_flags); + flags |= ImGuiInputTextFlags_CharsScientific; + return InputScalar(label, ImGuiDataType_Double, (void*)v, (void*)(step>0.0 ? &step : NULL), (void*)(step_fast>0.0 ? &step_fast : NULL), format, flags); } //------------------------------------------------------------------------- -// [SECTION] Widgets: InputText, InputTextMultiline +// [SECTION] Widgets: InputText, InputTextMultiline, InputTextWithHint //------------------------------------------------------------------------- // - InputText() +// - InputTextWithHint() // - InputTextMultiline() // - InputTextEx() [Internal] //------------------------------------------------------------------------- @@ -2810,12 +2894,18 @@ bool ImGui::InputDouble(const char* label, double* v, double step, double step_f bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) { IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline)); // call InputTextMultiline() - return InputTextEx(label, buf, (int)buf_size, ImVec2(0,0), flags, callback, user_data); + return InputTextEx(label, NULL, buf, (int)buf_size, ImVec2(0,0), flags, callback, user_data); } bool ImGui::InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) { - return InputTextEx(label, buf, (int)buf_size, size, flags | ImGuiInputTextFlags_Multiline, callback, user_data); + return InputTextEx(label, NULL, buf, (int)buf_size, size, flags | ImGuiInputTextFlags_Multiline, callback, user_data); +} + +bool ImGui::InputTextWithHint(const char* label, const char* hint, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) +{ + IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline)); // call InputTextMultiline() + return InputTextEx(label, hint, buf, (int)buf_size, ImVec2(0, 0), flags, callback, user_data); } static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end) @@ -2834,8 +2924,9 @@ static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining, ImVec2* out_offset, bool stop_on_new_line) { - ImFont* font = GImGui->Font; - const float line_height = GImGui->FontSize; + ImGuiContext& g = *GImGui; + ImFont* font = g.Font; + const float line_height = g.FontSize; const float scale = line_height / font->FontSize; ImVec2 text_size = ImVec2(0,0); @@ -2877,7 +2968,7 @@ static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* t } // Wrapper for stb_textedit.h to edit text (our wrapper is for: statically sized buffer, single-line, wchar characters. InputText converts between UTF-8 and wchar) -namespace ImGuiStb +namespace ImStb { static int STB_TEXTEDIT_STRINGLEN(const STB_TEXTEDIT_STRING* obj) { return obj->CurLenW; } @@ -2980,7 +3071,7 @@ static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const Im void ImGuiInputTextState::OnKeyPressed(int key) { - stb_textedit_key(this, &StbState, key); + stb_textedit_key(this, &Stb, key); CursorFollow = true; CursorAnimReset(); } @@ -3024,10 +3115,10 @@ void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, cons ImGuiContext& g = *GImGui; ImGuiInputTextState* edit_state = &g.InputTextState; IM_ASSERT(edit_state->ID != 0 && g.ActiveId == edit_state->ID); - IM_ASSERT(Buf == edit_state->TempBuffer.Data); + IM_ASSERT(Buf == edit_state->TextA.Data); int new_buf_size = BufTextLen + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1; - edit_state->TempBuffer.reserve(new_buf_size + 1); - Buf = edit_state->TempBuffer.Data; + edit_state->TextA.reserve(new_buf_size + 1); + Buf = edit_state->TextA.Data; BufSize = edit_state->BufCapacityA = new_buf_size; } @@ -3103,12 +3194,13 @@ static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags f // Edit a string of text // - buf_size account for the zero-terminator, so a buf_size of 6 can hold "Hello" but not "Hello!". -// This is so we can easily call InputText() on static arrays using ARRAYSIZE() and to match +// This is so we can easily call InputText() on static arrays using ARRAYSIZE() and to match // Note that in std::string world, capacity() would omit 1 byte used by the zero-terminator. // - When active, hold on a privately held copy of the text (and apply back to 'buf'). So changing 'buf' while the InputText is active has no effect. // - If you want to use ImGui::InputText() with std::string, see misc/cpp/imgui_stdlib.h -// (FIXME: Rather messy function partly because we are doing UTF8 > u16 > UTF8 conversions on the go to more easily handle stb_textedit calls. Ideally we should stay in UTF-8 all the time. See https://github.com/nothings/stb/issues/188) -bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* callback_user_data) +// (FIXME: Rather confusing and messy function, among the worse part of our codebase, expecting to rewrite a V2 at some point.. Partly because we are +// doing UTF8 > U16 > UTF8 conversions on the go to easily interface with stb_textedit. Ideally should stay in UTF-8 all the time. See https://github.com/nothings/stb/issues/188) +bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* callback_user_data) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) @@ -3118,18 +3210,19 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackCompletion) && (flags & ImGuiInputTextFlags_AllowTabInput))); // Can't use both together (they both use tab key) ImGuiContext& g = *GImGui; - const ImGuiIO& io = g.IO; + ImGuiIO& io = g.IO; const ImGuiStyle& style = g.Style; + const bool RENDER_SELECTION_WHEN_INACTIVE = false; const bool is_multiline = (flags & ImGuiInputTextFlags_Multiline) != 0; - const bool is_editable = (flags & ImGuiInputTextFlags_ReadOnly) == 0; + const bool is_readonly = (flags & ImGuiInputTextFlags_ReadOnly) != 0; const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0; const bool is_undoable = (flags & ImGuiInputTextFlags_NoUndoRedo) == 0; const bool is_resizable = (flags & ImGuiInputTextFlags_CallbackResize) != 0; if (is_resizable) IM_ASSERT(callback != NULL); // Must provide a callback if you set the ImGuiInputTextFlags_CallbackResize flag! - if (is_multiline) // Open group before calling GetID() because groups tracks id created within their scope, + if (is_multiline) // Open group before calling GetID() because groups tracks id created within their scope, BeginGroup(); const ImGuiID id = window->GetID(label); const ImVec2 label_size = CalcTextSize(label, NULL, true); @@ -3140,7 +3233,12 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 ImGuiWindow* draw_window = window; if (is_multiline) { - ItemAdd(total_bb, id, &frame_bb); + if (!ItemAdd(total_bb, id, &frame_bb)) + { + ItemSize(total_bb, style.FramePadding.y); + EndGroup(); + return false; + } if (!BeginChildFrame(id, frame_bb.GetSize())) { EndChildFrame(); @@ -3161,109 +3259,143 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 if (hovered) g.MouseCursor = ImGuiMouseCursor_TextInput; - // Password pushes a temporary font with only a fallback glyph - if (is_password) - { - const ImFontGlyph* glyph = g.Font->FindGlyph('*'); - ImFont* password_font = &g.InputTextPasswordFont; - password_font->FontSize = g.Font->FontSize; - password_font->Scale = g.Font->Scale; - password_font->DisplayOffset = g.Font->DisplayOffset; - password_font->Ascent = g.Font->Ascent; - password_font->Descent = g.Font->Descent; - password_font->ContainerAtlas = g.Font->ContainerAtlas; - password_font->FallbackGlyph = glyph; - password_font->FallbackAdvanceX = glyph->AdvanceX; - IM_ASSERT(password_font->Glyphs.empty() && password_font->IndexAdvanceX.empty() && password_font->IndexLookup.empty()); - PushFont(password_font); - } - // NB: we are only allowed to access 'edit_state' if we are the active widget. - ImGuiInputTextState& edit_state = g.InputTextState; + ImGuiInputTextState* state = NULL; + if (g.InputTextState.ID == id) + state = &g.InputTextState; - const bool focus_requested = FocusableItemRegister(window, id, (flags & (ImGuiInputTextFlags_CallbackCompletion|ImGuiInputTextFlags_AllowTabInput)) == 0); // Using completion callback disable keyboard tabbing - const bool focus_requested_by_code = focus_requested && (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent); + const bool focus_requested = FocusableItemRegister(window, id); + const bool focus_requested_by_code = focus_requested && (g.FocusRequestCurrWindow == window && g.FocusRequestCurrCounterAll == window->DC.FocusCounterAll); const bool focus_requested_by_tab = focus_requested && !focus_requested_by_code; const bool user_clicked = hovered && io.MouseClicked[0]; - const bool user_scrolled = is_multiline && g.ActiveId == 0 && edit_state.ID == id && g.ActiveIdPreviousFrame == draw_window->GetIDNoKeepAlive("#SCROLLY"); const bool user_nav_input_start = (g.ActiveId != id) && ((g.NavInputId == id) || (g.NavActivateId == id && g.NavInputSource == ImGuiInputSource_NavKeyboard)); + const bool user_scroll_finish = is_multiline && state != NULL && g.ActiveId == 0 && g.ActiveIdPreviousFrame == GetScrollbarID(draw_window, ImGuiAxis_Y); + const bool user_scroll_active = is_multiline && state != NULL && g.ActiveId == GetScrollbarID(draw_window, ImGuiAxis_Y); bool clear_active_id = false; - bool select_all = (g.ActiveId != id) && ((flags & ImGuiInputTextFlags_AutoSelectAll) != 0 || user_nav_input_start) && (!is_multiline); - if (focus_requested || user_clicked || user_scrolled || user_nav_input_start) + + const bool init_make_active = (focus_requested || user_clicked || user_scroll_finish || user_nav_input_start); + const bool init_state = (init_make_active || user_scroll_active); + if (init_state && g.ActiveId != id) { - if (g.ActiveId != id) + // Access state even if we don't own it yet. + state = &g.InputTextState; + state->CursorAnimReset(); + + // Take a copy of the initial buffer value (both in original UTF-8 format and converted to wchar) + // From the moment we focused we are ignoring the content of 'buf' (unless we are in read-only mode) + const int buf_len = (int)strlen(buf); + state->InitialTextA.resize(buf_len + 1); // UTF-8. we use +1 to make sure that .Data is always pointing to at least an empty string. + memcpy(state->InitialTextA.Data, buf, buf_len + 1); + + // Start edition + const char* buf_end = NULL; + state->TextW.resize(buf_size + 1); // wchar count <= UTF-8 count. we use +1 to make sure that .Data is always pointing to at least an empty string. + state->TextA.resize(0); + state->TextAIsValid = false; // TextA is not valid yet (we will display buf until then) + state->CurLenW = ImTextStrFromUtf8(state->TextW.Data, buf_size, buf, NULL, &buf_end); + state->CurLenA = (int)(buf_end - buf); // We can't get the result from ImStrncpy() above because it is not UTF-8 aware. Here we'll cut off malformed UTF-8. + + // Preserve cursor position and undo/redo stack if we come back to same widget + // FIXME: For non-readonly widgets we might be able to require that TextAIsValid && TextA == buf ? (untested) and discard undo stack if user buffer has changed. + const bool recycle_state = (state->ID == id); + if (recycle_state) { - // Start edition - // Take a copy of the initial buffer value (both in original UTF-8 format and converted to wchar) - // From the moment we focused we are ignoring the content of 'buf' (unless we are in read-only mode) - const int prev_len_w = edit_state.CurLenW; - const int init_buf_len = (int)strlen(buf); - edit_state.TextW.resize(buf_size+1); // wchar count <= UTF-8 count. we use +1 to make sure that .Data isn't NULL so it doesn't crash. - edit_state.InitialText.resize(init_buf_len + 1); // UTF-8. we use +1 to make sure that .Data isn't NULL so it doesn't crash. - memcpy(edit_state.InitialText.Data, buf, init_buf_len + 1); - const char* buf_end = NULL; - edit_state.CurLenW = ImTextStrFromUtf8(edit_state.TextW.Data, buf_size, buf, NULL, &buf_end); - edit_state.CurLenA = (int)(buf_end - buf); // We can't get the result from ImStrncpy() above because it is not UTF-8 aware. Here we'll cut off malformed UTF-8. - edit_state.CursorAnimReset(); - - // Preserve cursor position and undo/redo stack if we come back to same widget - // FIXME: We should probably compare the whole buffer to be on the safety side. Comparing buf (utf8) and edit_state.Text (wchar). - const bool recycle_state = (edit_state.ID == id) && (prev_len_w == edit_state.CurLenW); - if (recycle_state) - { - // Recycle existing cursor/selection/undo stack but clamp position - // Note a single mouse click will override the cursor/position immediately by calling stb_textedit_click handler. - edit_state.CursorClamp(); - } - else - { - edit_state.ID = id; - edit_state.ScrollX = 0.0f; - stb_textedit_initialize_state(&edit_state.StbState, !is_multiline); - if (!is_multiline && focus_requested_by_code) - select_all = true; - } - if (flags & ImGuiInputTextFlags_AlwaysInsertMode) - edit_state.StbState.insert_mode = true; - if (!is_multiline && (focus_requested_by_tab || (user_clicked && io.KeyCtrl))) + // Recycle existing cursor/selection/undo stack but clamp position + // Note a single mouse click will override the cursor/position immediately by calling stb_textedit_click handler. + state->CursorClamp(); + } + else + { + state->ID = id; + state->ScrollX = 0.0f; + stb_textedit_initialize_state(&state->Stb, !is_multiline); + if (!is_multiline && focus_requested_by_code) select_all = true; } + if (flags & ImGuiInputTextFlags_AlwaysInsertMode) + state->Stb.insert_mode = 1; + if (!is_multiline && (focus_requested_by_tab || (user_clicked && io.KeyCtrl))) + select_all = true; + } + + if (g.ActiveId != id && init_make_active) + { + IM_ASSERT(state && state->ID == id); SetActiveID(id, window); SetFocusID(id, window); FocusWindow(window); + IM_ASSERT(ImGuiNavInput_COUNT < 32); + g.ActiveIdBlockNavInputFlags = (1 << ImGuiNavInput_Cancel); + if (flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_AllowTabInput)) // Disable keyboard tabbing out + g.ActiveIdBlockNavInputFlags |= (1 << ImGuiNavInput_KeyTab_); if (!is_multiline && !(flags & ImGuiInputTextFlags_CallbackHistory)) - g.ActiveIdAllowNavDirFlags |= ((1 << ImGuiDir_Up) | (1 << ImGuiDir_Down)); + g.ActiveIdAllowNavDirFlags = ((1 << ImGuiDir_Up) | (1 << ImGuiDir_Down)); } - else if (io.MouseClicked[0]) - { - // Release focus when we click outside + + // We have an edge case if ActiveId was set through another widget (e.g. widget being swapped), clear id immediately (don't wait until the end of the function) + if (g.ActiveId == id && state == NULL) + ClearActiveID(); + + // Release focus when we click outside + if (g.ActiveId == id && io.MouseClicked[0] && !init_state && !init_make_active) //-V560 clear_active_id = true; + + // When read-only we always use the live data passed to the function + // FIXME-OPT: Because our selection/cursor code currently needs the wide text we need to convert it when active, which is not ideal :( + if (is_readonly && state != NULL) + { + const bool will_render_cursor = (g.ActiveId == id) || (user_scroll_active); + const bool will_render_selection = state->HasSelection() && (RENDER_SELECTION_WHEN_INACTIVE || will_render_cursor); + if (will_render_cursor || will_render_selection) + { + const char* buf_end = NULL; + state->TextW.resize(buf_size + 1); + state->CurLenW = ImTextStrFromUtf8(state->TextW.Data, state->TextW.Size, buf, NULL, &buf_end); + state->CurLenA = (int)(buf_end - buf); + state->CursorClamp(); + } } + // Lock the decision of whether we are going to take the path displaying the cursor or selection + const bool render_cursor = (g.ActiveId == id) || (state && user_scroll_active); + const bool render_selection = state && state->HasSelection() && (RENDER_SELECTION_WHEN_INACTIVE || render_cursor); bool value_changed = false; bool enter_pressed = false; - int backup_current_text_length = 0; - if (g.ActiveId == id) + // Select the buffer to render. + const bool buf_display_from_state = (render_cursor || render_selection || g.ActiveId == id) && !is_readonly && state && state->TextAIsValid; + const bool is_displaying_hint = (hint != NULL && (buf_display_from_state ? state->TextA.Data : buf)[0] == 0); + + // Password pushes a temporary font with only a fallback glyph + if (is_password && !is_displaying_hint) { - if (!is_editable && !g.ActiveIdIsJustActivated) - { - // When read-only we always use the live data passed to the function - edit_state.TextW.resize(buf_size+1); - const char* buf_end = NULL; - edit_state.CurLenW = ImTextStrFromUtf8(edit_state.TextW.Data, edit_state.TextW.Size, buf, NULL, &buf_end); - edit_state.CurLenA = (int)(buf_end - buf); - edit_state.CursorClamp(); - } + const ImFontGlyph* glyph = g.Font->FindGlyph('*'); + ImFont* password_font = &g.InputTextPasswordFont; + password_font->FontSize = g.Font->FontSize; + password_font->Scale = g.Font->Scale; + password_font->DisplayOffset = g.Font->DisplayOffset; + password_font->Ascent = g.Font->Ascent; + password_font->Descent = g.Font->Descent; + password_font->ContainerAtlas = g.Font->ContainerAtlas; + password_font->FallbackGlyph = glyph; + password_font->FallbackAdvanceX = glyph->AdvanceX; + IM_ASSERT(password_font->Glyphs.empty() && password_font->IndexAdvanceX.empty() && password_font->IndexLookup.empty()); + PushFont(password_font); + } - backup_current_text_length = edit_state.CurLenA; - edit_state.BufCapacityA = buf_size; - edit_state.UserFlags = flags; - edit_state.UserCallback = callback; - edit_state.UserCallbackData = callback_user_data; + // Process mouse inputs and character inputs + int backup_current_text_length = 0; + if (g.ActiveId == id) + { + IM_ASSERT(state != NULL); + backup_current_text_length = state->CurLenA; + state->BufCapacityA = buf_size; + state->UserFlags = flags; + state->UserCallback = callback; + state->UserCallbackData = callback_user_data; // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget. // Down the line we should have a cleaner library-wide concept of Selected vs Active. @@ -3271,61 +3403,62 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 g.WantTextInputNextFrame = 1; // Edit in progress - const float mouse_x = (io.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + edit_state.ScrollX; + const float mouse_x = (io.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + state->ScrollX; const float mouse_y = (is_multiline ? (io.MousePos.y - draw_window->DC.CursorPos.y - style.FramePadding.y) : (g.FontSize*0.5f)); const bool is_osx = io.ConfigMacOSXBehaviors; if (select_all || (hovered && !is_osx && io.MouseDoubleClicked[0])) { - edit_state.SelectAll(); - edit_state.SelectedAllMouseLock = true; + state->SelectAll(); + state->SelectedAllMouseLock = true; } else if (hovered && is_osx && io.MouseDoubleClicked[0]) { // Double-click select a word only, OS X style (by simulating keystrokes) - edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT); - edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT); + state->OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT); + state->OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT); } - else if (io.MouseClicked[0] && !edit_state.SelectedAllMouseLock) + else if (io.MouseClicked[0] && !state->SelectedAllMouseLock) { if (hovered) { - stb_textedit_click(&edit_state, &edit_state.StbState, mouse_x, mouse_y); - edit_state.CursorAnimReset(); + stb_textedit_click(state, &state->Stb, mouse_x, mouse_y); + state->CursorAnimReset(); } } - else if (io.MouseDown[0] && !edit_state.SelectedAllMouseLock && (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f)) + else if (io.MouseDown[0] && !state->SelectedAllMouseLock && (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f)) { - stb_textedit_drag(&edit_state, &edit_state.StbState, mouse_x, mouse_y); - edit_state.CursorAnimReset(); - edit_state.CursorFollow = true; + stb_textedit_drag(state, &state->Stb, mouse_x, mouse_y); + state->CursorAnimReset(); + state->CursorFollow = true; } - if (edit_state.SelectedAllMouseLock && !io.MouseDown[0]) - edit_state.SelectedAllMouseLock = false; + if (state->SelectedAllMouseLock && !io.MouseDown[0]) + state->SelectedAllMouseLock = false; - if (io.InputCharacters[0]) + if (io.InputQueueCharacters.Size > 0) { // Process text input (before we check for Return because using some IME will effectively send a Return?) // We ignore CTRL inputs, but need to allow ALT+CTRL as some keyboards (e.g. German) use AltGR (which _is_ Alt+Ctrl) to input certain characters. bool ignore_inputs = (io.KeyCtrl && !io.KeyAlt) || (is_osx && io.KeySuper); - if (!ignore_inputs && is_editable && !user_nav_input_start) - for (int n = 0; n < IM_ARRAYSIZE(io.InputCharacters) && io.InputCharacters[n]; n++) + if (!ignore_inputs && !is_readonly && !user_nav_input_start) + for (int n = 0; n < io.InputQueueCharacters.Size; n++) { // Insert character if they pass filtering - unsigned int c = (unsigned int)io.InputCharacters[n]; + unsigned int c = (unsigned int)io.InputQueueCharacters[n]; if (InputTextFilterCharacter(&c, flags, callback, callback_user_data)) - edit_state.OnKeyPressed((int)c); + state->OnKeyPressed((int)c); } // Consume characters - memset(g.IO.InputCharacters, 0, sizeof(g.IO.InputCharacters)); + io.InputQueueCharacters.resize(0); } } + // Process other shortcuts/key-presses bool cancel_edit = false; if (g.ActiveId == id && !g.ActiveIdIsJustActivated && !clear_active_id) { - // Handle key-presses + IM_ASSERT(state != NULL); const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0); const bool is_osx = io.ConfigMacOSXBehaviors; const bool is_shortcut_key = (is_osx ? (io.KeySuper && !io.KeyCtrl) : (io.KeyCtrl && !io.KeySuper)) && !io.KeyAlt && !io.KeyShift; // OS X style: Shortcuts using Cmd/Super instead of Ctrl @@ -3335,27 +3468,29 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 const bool is_ctrl_key_only = io.KeyCtrl && !io.KeyShift && !io.KeyAlt && !io.KeySuper; const bool is_shift_key_only = io.KeyShift && !io.KeyCtrl && !io.KeyAlt && !io.KeySuper; - const bool is_cut = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_X)) || (is_shift_key_only && IsKeyPressedMap(ImGuiKey_Delete))) && is_editable && !is_password && (!is_multiline || edit_state.HasSelection()); - const bool is_copy = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_C)) || (is_ctrl_key_only && IsKeyPressedMap(ImGuiKey_Insert))) && !is_password && (!is_multiline || edit_state.HasSelection()); - const bool is_paste = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_V)) || (is_shift_key_only && IsKeyPressedMap(ImGuiKey_Insert))) && is_editable; - const bool is_undo = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Z)) && is_editable && is_undoable); - const bool is_redo = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Y)) || (is_osx_shift_shortcut && IsKeyPressedMap(ImGuiKey_Z))) && is_editable && is_undoable; - - if (IsKeyPressedMap(ImGuiKey_LeftArrow)) { edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_RightArrow)) { edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_UpArrow) && is_multiline) { if (io.KeyCtrl) SetWindowScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f)); else edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_DownArrow) && is_multiline) { if (io.KeyCtrl) SetWindowScrollY(draw_window, ImMin(draw_window->Scroll.y + g.FontSize, GetScrollMaxY())); else edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTEND : STB_TEXTEDIT_K_DOWN) | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_Home)) { edit_state.OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_End)) { edit_state.OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_Delete) && is_editable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_Backspace) && is_editable) + const bool is_cut = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_X)) || (is_shift_key_only && IsKeyPressedMap(ImGuiKey_Delete))) && !is_readonly && !is_password && (!is_multiline || state->HasSelection()); + const bool is_copy = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_C)) || (is_ctrl_key_only && IsKeyPressedMap(ImGuiKey_Insert))) && !is_password && (!is_multiline || state->HasSelection()); + const bool is_paste = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_V)) || (is_shift_key_only && IsKeyPressedMap(ImGuiKey_Insert))) && !is_readonly; + const bool is_undo = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Z)) && !is_readonly && is_undoable); + const bool is_redo = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Y)) || (is_osx_shift_shortcut && IsKeyPressedMap(ImGuiKey_Z))) && !is_readonly && is_undoable; + + if (IsKeyPressedMap(ImGuiKey_LeftArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_RightArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_UpArrow) && is_multiline) { if (io.KeyCtrl) SetWindowScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f)); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_DownArrow) && is_multiline) { if (io.KeyCtrl) SetWindowScrollY(draw_window, ImMin(draw_window->Scroll.y + g.FontSize, GetScrollMaxY())); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTEND : STB_TEXTEDIT_K_DOWN) | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_Home)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_End)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_Delete) && !is_readonly) { state->OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_Backspace) && !is_readonly) { - if (!edit_state.HasSelection()) + if (!state->HasSelection()) { - if (is_wordmove_key_down) edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT|STB_TEXTEDIT_K_SHIFT); - else if (is_osx && io.KeySuper && !io.KeyAlt && !io.KeyCtrl) edit_state.OnKeyPressed(STB_TEXTEDIT_K_LINESTART|STB_TEXTEDIT_K_SHIFT); + if (is_wordmove_key_down) + state->OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT|STB_TEXTEDIT_K_SHIFT); + else if (is_osx && io.KeySuper && !io.KeyAlt && !io.KeyCtrl) + state->OnKeyPressed(STB_TEXTEDIT_K_LINESTART|STB_TEXTEDIT_K_SHIFT); } - edit_state.OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask); + state->OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask); } else if (IsKeyPressedMap(ImGuiKey_Enter)) { @@ -3364,18 +3499,18 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 { enter_pressed = clear_active_id = true; } - else if (is_editable) + else if (!is_readonly) { unsigned int c = '\n'; // Insert new line if (InputTextFilterCharacter(&c, flags, callback, callback_user_data)) - edit_state.OnKeyPressed((int)c); + state->OnKeyPressed((int)c); } } - else if ((flags & ImGuiInputTextFlags_AllowTabInput) && IsKeyPressedMap(ImGuiKey_Tab) && !io.KeyCtrl && !io.KeyShift && !io.KeyAlt && is_editable) + else if ((flags & ImGuiInputTextFlags_AllowTabInput) && IsKeyPressedMap(ImGuiKey_Tab) && !io.KeyCtrl && !io.KeyShift && !io.KeyAlt && !is_readonly) { unsigned int c = '\t'; // Insert TAB if (InputTextFilterCharacter(&c, flags, callback, callback_user_data)) - edit_state.OnKeyPressed((int)c); + state->OnKeyPressed((int)c); } else if (IsKeyPressedMap(ImGuiKey_Escape)) { @@ -3383,31 +3518,33 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 } else if (is_undo || is_redo) { - edit_state.OnKeyPressed(is_undo ? STB_TEXTEDIT_K_UNDO : STB_TEXTEDIT_K_REDO); - edit_state.ClearSelection(); + state->OnKeyPressed(is_undo ? STB_TEXTEDIT_K_UNDO : STB_TEXTEDIT_K_REDO); + state->ClearSelection(); } else if (is_shortcut_key && IsKeyPressedMap(ImGuiKey_A)) { - edit_state.SelectAll(); - edit_state.CursorFollow = true; + state->SelectAll(); + state->CursorFollow = true; } else if (is_cut || is_copy) { // Cut, Copy if (io.SetClipboardTextFn) { - const int ib = edit_state.HasSelection() ? ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end) : 0; - const int ie = edit_state.HasSelection() ? ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end) : edit_state.CurLenW; - edit_state.TempBuffer.resize((ie-ib) * 4 + 1); - ImTextStrToUtf8(edit_state.TempBuffer.Data, edit_state.TempBuffer.Size, edit_state.TextW.Data+ib, edit_state.TextW.Data+ie); - SetClipboardText(edit_state.TempBuffer.Data); + const int ib = state->HasSelection() ? ImMin(state->Stb.select_start, state->Stb.select_end) : 0; + const int ie = state->HasSelection() ? ImMax(state->Stb.select_start, state->Stb.select_end) : state->CurLenW; + const int clipboard_data_len = ImTextCountUtf8BytesFromStr(state->TextW.Data + ib, state->TextW.Data + ie) + 1; + char* clipboard_data = (char*)MemAlloc(clipboard_data_len * sizeof(char)); + ImTextStrToUtf8(clipboard_data, clipboard_data_len, state->TextW.Data + ib, state->TextW.Data + ie); + SetClipboardText(clipboard_data); + MemFree(clipboard_data); } if (is_cut) { - if (!edit_state.HasSelection()) - edit_state.SelectAll(); - edit_state.CursorFollow = true; - stb_textedit_cut(&edit_state, &edit_state.StbState); + if (!state->HasSelection()) + state->SelectAll(); + state->CursorFollow = true; + stb_textedit_cut(state, &state->Stb); } } else if (is_paste) @@ -3431,25 +3568,27 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 clipboard_filtered[clipboard_filtered_len] = 0; if (clipboard_filtered_len > 0) // If everything was filtered, ignore the pasting operation { - stb_textedit_paste(&edit_state, &edit_state.StbState, clipboard_filtered, clipboard_filtered_len); - edit_state.CursorFollow = true; + stb_textedit_paste(state, &state->Stb, clipboard_filtered, clipboard_filtered_len); + state->CursorFollow = true; } MemFree(clipboard_filtered); } } } + // Process callbacks and apply result back to user's buffer. if (g.ActiveId == id) { + IM_ASSERT(state != NULL); const char* apply_new_text = NULL; int apply_new_text_length = 0; if (cancel_edit) { // Restore initial value. Only return true if restoring to the initial value changes the current buffer contents. - if (is_editable && strcmp(buf, edit_state.InitialText.Data) != 0) + if (!is_readonly && strcmp(buf, state->InitialTextA.Data) != 0) { - apply_new_text = edit_state.InitialText.Data; - apply_new_text_length = edit_state.InitialText.Size - 1; + apply_new_text = state->InitialTextA.Data; + apply_new_text_length = state->InitialTextA.Size - 1; } } @@ -3462,10 +3601,11 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 // Note that as soon as the input box is active, the in-widget value gets priority over any underlying modification of the input buffer // FIXME: We actually always render 'buf' when calling DrawList->AddText, making the comment above incorrect. // FIXME-OPT: CPU waste to do this every time the widget is active, should mark dirty state from the stb_textedit callbacks. - if (is_editable) + if (!is_readonly) { - edit_state.TempBuffer.resize(edit_state.TextW.Size * 4 + 1); - ImTextStrToUtf8(edit_state.TempBuffer.Data, edit_state.TempBuffer.Size, edit_state.TextW.Data, NULL); + state->TextAIsValid = true; + state->TextA.resize(state->TextW.Size * 4 + 1); + ImTextStrToUtf8(state->TextA.Data, state->TextA.Size, state->TextW.Data, NULL); } // User callback @@ -3503,44 +3643,44 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 callback_data.UserData = callback_user_data; callback_data.EventKey = event_key; - callback_data.Buf = edit_state.TempBuffer.Data; - callback_data.BufTextLen = edit_state.CurLenA; - callback_data.BufSize = edit_state.BufCapacityA; + callback_data.Buf = state->TextA.Data; + callback_data.BufTextLen = state->CurLenA; + callback_data.BufSize = state->BufCapacityA; callback_data.BufDirty = false; // We have to convert from wchar-positions to UTF-8-positions, which can be pretty slow (an incentive to ditch the ImWchar buffer, see https://github.com/nothings/stb/issues/188) - ImWchar* text = edit_state.TextW.Data; - const int utf8_cursor_pos = callback_data.CursorPos = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.cursor); - const int utf8_selection_start = callback_data.SelectionStart = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_start); - const int utf8_selection_end = callback_data.SelectionEnd = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_end); + ImWchar* text = state->TextW.Data; + const int utf8_cursor_pos = callback_data.CursorPos = ImTextCountUtf8BytesFromStr(text, text + state->Stb.cursor); + const int utf8_selection_start = callback_data.SelectionStart = ImTextCountUtf8BytesFromStr(text, text + state->Stb.select_start); + const int utf8_selection_end = callback_data.SelectionEnd = ImTextCountUtf8BytesFromStr(text, text + state->Stb.select_end); // Call user code callback(&callback_data); // Read back what user may have modified - IM_ASSERT(callback_data.Buf == edit_state.TempBuffer.Data); // Invalid to modify those fields - IM_ASSERT(callback_data.BufSize == edit_state.BufCapacityA); + IM_ASSERT(callback_data.Buf == state->TextA.Data); // Invalid to modify those fields + IM_ASSERT(callback_data.BufSize == state->BufCapacityA); IM_ASSERT(callback_data.Flags == flags); - if (callback_data.CursorPos != utf8_cursor_pos) { edit_state.StbState.cursor = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.CursorPos); edit_state.CursorFollow = true; } - if (callback_data.SelectionStart != utf8_selection_start) { edit_state.StbState.select_start = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionStart); } - if (callback_data.SelectionEnd != utf8_selection_end) { edit_state.StbState.select_end = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionEnd); } + if (callback_data.CursorPos != utf8_cursor_pos) { state->Stb.cursor = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.CursorPos); state->CursorFollow = true; } + if (callback_data.SelectionStart != utf8_selection_start) { state->Stb.select_start = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionStart); } + if (callback_data.SelectionEnd != utf8_selection_end) { state->Stb.select_end = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionEnd); } if (callback_data.BufDirty) { IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text! if (callback_data.BufTextLen > backup_current_text_length && is_resizable) - edit_state.TextW.resize(edit_state.TextW.Size + (callback_data.BufTextLen - backup_current_text_length)); - edit_state.CurLenW = ImTextStrFromUtf8(edit_state.TextW.Data, edit_state.TextW.Size, callback_data.Buf, NULL); - edit_state.CurLenA = callback_data.BufTextLen; // Assume correct length and valid UTF-8 from user, saves us an extra strlen() - edit_state.CursorAnimReset(); + state->TextW.resize(state->TextW.Size + (callback_data.BufTextLen - backup_current_text_length)); + state->CurLenW = ImTextStrFromUtf8(state->TextW.Data, state->TextW.Size, callback_data.Buf, NULL); + state->CurLenA = callback_data.BufTextLen; // Assume correct length and valid UTF-8 from user, saves us an extra strlen() + state->CursorAnimReset(); } } } // Will copy result string if modified - if (is_editable && strcmp(edit_state.TempBuffer.Data, buf) != 0) + if (!is_readonly && strcmp(state->TextA.Data, buf) != 0) { - apply_new_text = edit_state.TempBuffer.Data; - apply_new_text_length = edit_state.CurLenA; + apply_new_text = state->TextA.Data; + apply_new_text_length = state->CurLenA; } } @@ -3565,63 +3705,76 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 } // If the underlying buffer resize was denied or not carried to the next frame, apply_new_text_length+1 may be >= buf_size. - ImStrncpy(buf, edit_state.TempBuffer.Data, ImMin(apply_new_text_length + 1, buf_size)); + ImStrncpy(buf, apply_new_text, ImMin(apply_new_text_length + 1, buf_size)); value_changed = true; } // Clear temporary user storage - edit_state.UserFlags = 0; - edit_state.UserCallback = NULL; - edit_state.UserCallbackData = NULL; + state->UserFlags = 0; + state->UserCallback = NULL; + state->UserCallbackData = NULL; } // Release active ID at the end of the function (so e.g. pressing Return still does a final application of the value) if (clear_active_id && g.ActiveId == id) ClearActiveID(); - // Render - // Select which buffer we are going to display. When ImGuiInputTextFlags_NoLiveEdit is set 'buf' might still be the old value. We set buf to NULL to prevent accidental usage from now on. - const char* buf_display = (g.ActiveId == id && is_editable) ? edit_state.TempBuffer.Data : buf; buf = NULL; + // Render frame + if (!is_multiline) + { + RenderNavHighlight(frame_bb, id); + RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); + } + + const ImVec4 clip_rect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + size.x, frame_bb.Min.y + size.y); // Not using frame_bb.Max because we have adjusted size + ImVec2 draw_pos = is_multiline ? draw_window->DC.CursorPos : frame_bb.Min + style.FramePadding; + ImVec2 text_size(0.0f, 0.0f); // Set upper limit of single-line InputTextEx() at 2 million characters strings. The current pathological worst case is a long line // without any carriage return, which would makes ImFont::RenderText() reserve too many vertices and probably crash. Avoid it altogether. // Note that we only use this limit on single-line InputText(), so a pathologically large line on a InputTextMultiline() would still crash. const int buf_display_max_length = 2 * 1024 * 1024; - - if (!is_multiline) + const char* buf_display = buf_display_from_state ? state->TextA.Data : buf; //-V595 + const char* buf_display_end = NULL; // We have specialized paths below for setting the length + if (is_displaying_hint) { - RenderNavHighlight(frame_bb, id); - RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); + buf_display = hint; + buf_display_end = hint + strlen(hint); } - const ImVec4 clip_rect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + size.x, frame_bb.Min.y + size.y); // Not using frame_bb.Max because we have adjusted size - ImVec2 render_pos = is_multiline ? draw_window->DC.CursorPos : frame_bb.Min + style.FramePadding; - ImVec2 text_size(0.f, 0.f); - const bool is_currently_scrolling = (edit_state.ID == id && is_multiline && g.ActiveId == draw_window->GetIDNoKeepAlive("#SCROLLY")); - if (g.ActiveId == id || is_currently_scrolling) + // Render text. We currently only render selection when the widget is active or while scrolling. + // FIXME: We could remove the '&& render_cursor' to keep rendering selection when inactive. + if (render_cursor || render_selection) { - edit_state.CursorAnim += io.DeltaTime; + IM_ASSERT(state != NULL); + if (!is_displaying_hint) + buf_display_end = buf_display + state->CurLenA; + // Render text (with cursor and selection) // This is going to be messy. We need to: // - Display the text (this alone can be more easily clipped) // - Handle scrolling, highlight selection, display cursor (those all requires some form of 1d->2d cursor position calculation) // - Measure text height (for scrollbar) // We are attempting to do most of that in **one main pass** to minimize the computation cost (non-negligible for large amount of text) + 2nd pass for selection rendering (we could merge them by an extra refactoring effort) // FIXME: This should occur on buf_display but we'd need to maintain cursor/select_start/select_end for UTF-8. - const ImWchar* text_begin = edit_state.TextW.Data; + const ImWchar* text_begin = state->TextW.Data; ImVec2 cursor_offset, select_start_offset; { - // Count lines + find lines numbers straddling 'cursor' and 'select_start' position. - const ImWchar* searches_input_ptr[2]; - searches_input_ptr[0] = text_begin + edit_state.StbState.cursor; - searches_input_ptr[1] = NULL; - int searches_remaining = 1; - int searches_result_line_number[2] = { -1, -999 }; - if (edit_state.StbState.select_start != edit_state.StbState.select_end) + // Find lines numbers straddling 'cursor' (slot 0) and 'select_start' (slot 1) positions. + const ImWchar* searches_input_ptr[2] = { NULL, NULL }; + int searches_result_line_no[2] = { -1000, -1000 }; + int searches_remaining = 0; + if (render_cursor) + { + searches_input_ptr[0] = text_begin + state->Stb.cursor; + searches_result_line_no[0] = -1; + searches_remaining++; + } + if (render_selection) { - searches_input_ptr[1] = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end); - searches_result_line_number[1] = -1; + searches_input_ptr[1] = text_begin + ImMin(state->Stb.select_start, state->Stb.select_end); + searches_result_line_no[1] = -1; searches_remaining++; } @@ -3634,20 +3787,22 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 if (*s == '\n') { line_count++; - if (searches_result_line_number[0] == -1 && s >= searches_input_ptr[0]) { searches_result_line_number[0] = line_count; if (--searches_remaining <= 0) break; } - if (searches_result_line_number[1] == -1 && s >= searches_input_ptr[1]) { searches_result_line_number[1] = line_count; if (--searches_remaining <= 0) break; } + if (searches_result_line_no[0] == -1 && s >= searches_input_ptr[0]) { searches_result_line_no[0] = line_count; if (--searches_remaining <= 0) break; } + if (searches_result_line_no[1] == -1 && s >= searches_input_ptr[1]) { searches_result_line_no[1] = line_count; if (--searches_remaining <= 0) break; } } line_count++; - if (searches_result_line_number[0] == -1) searches_result_line_number[0] = line_count; - if (searches_result_line_number[1] == -1) searches_result_line_number[1] = line_count; + if (searches_result_line_no[0] == -1) + searches_result_line_no[0] = line_count; + if (searches_result_line_no[1] == -1) + searches_result_line_no[1] = line_count; // Calculate 2d position by finding the beginning of the line and measuring distance cursor_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[0], text_begin), searches_input_ptr[0]).x; - cursor_offset.y = searches_result_line_number[0] * g.FontSize; - if (searches_result_line_number[1] >= 0) + cursor_offset.y = searches_result_line_no[0] * g.FontSize; + if (searches_result_line_no[1] >= 0) { select_start_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[1], text_begin), searches_input_ptr[1]).x; - select_start_offset.y = searches_result_line_number[1] * g.FontSize; + select_start_offset.y = searches_result_line_no[1] * g.FontSize; } // Store text height (note that we haven't calculated text width at all, see GitHub issues #383, #1224) @@ -3656,20 +3811,20 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 } // Scroll - if (edit_state.CursorFollow) + if (render_cursor && state->CursorFollow) { // Horizontal scroll in chunks of quarter width if (!(flags & ImGuiInputTextFlags_NoHorizontalScroll)) { const float scroll_increment_x = size.x * 0.25f; - if (cursor_offset.x < edit_state.ScrollX) - edit_state.ScrollX = (float)(int)ImMax(0.0f, cursor_offset.x - scroll_increment_x); - else if (cursor_offset.x - size.x >= edit_state.ScrollX) - edit_state.ScrollX = (float)(int)(cursor_offset.x - size.x + scroll_increment_x); + if (cursor_offset.x < state->ScrollX) + state->ScrollX = (float)(int)ImMax(0.0f, cursor_offset.x - scroll_increment_x); + else if (cursor_offset.x - size.x >= state->ScrollX) + state->ScrollX = (float)(int)(cursor_offset.x - size.x + scroll_increment_x); } else { - edit_state.ScrollX = 0.0f; + state->ScrollX = 0.0f; } // Vertical scroll @@ -3680,24 +3835,25 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize); else if (cursor_offset.y - size.y >= scroll_y) scroll_y = cursor_offset.y - size.y; - draw_window->DC.CursorPos.y += (draw_window->Scroll.y - scroll_y); // To avoid a frame of lag + draw_window->DC.CursorPos.y += (draw_window->Scroll.y - scroll_y); // Manipulate cursor pos immediately avoid a frame of lag draw_window->Scroll.y = scroll_y; - render_pos.y = draw_window->DC.CursorPos.y; + draw_pos.y = draw_window->DC.CursorPos.y; } + + state->CursorFollow = false; } - edit_state.CursorFollow = false; - const ImVec2 render_scroll = ImVec2(edit_state.ScrollX, 0.0f); // Draw selection - if (edit_state.StbState.select_start != edit_state.StbState.select_end) + const ImVec2 draw_scroll = ImVec2(state->ScrollX, 0.0f); + if (render_selection) { - const ImWchar* text_selected_begin = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end); - const ImWchar* text_selected_end = text_begin + ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end); + const ImWchar* text_selected_begin = text_begin + ImMin(state->Stb.select_start, state->Stb.select_end); + const ImWchar* text_selected_end = text_begin + ImMax(state->Stb.select_start, state->Stb.select_end); + ImU32 bg_color = GetColorU32(ImGuiCol_TextSelectedBg, render_cursor ? 1.0f : 0.6f); // FIXME: current code flow mandate that render_cursor is always true here, we are leaving the transparent one for tests. float bg_offy_up = is_multiline ? 0.0f : -1.0f; // FIXME: those offsets should be part of the style? they don't play so well with multi-line selection. float bg_offy_dn = is_multiline ? 0.0f : 2.0f; - ImU32 bg_color = GetColorU32(ImGuiCol_TextSelectedBg); - ImVec2 rect_pos = render_pos + select_start_offset - render_scroll; + ImVec2 rect_pos = draw_pos + select_start_offset - draw_scroll; for (const ImWchar* p = text_selected_begin; p < text_selected_end; ) { if (rect_pos.y > clip_rect.w + g.FontSize) @@ -3719,36 +3875,48 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 if (rect.Overlaps(clip_rect)) draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color); } - rect_pos.x = render_pos.x - render_scroll.x; + rect_pos.x = draw_pos.x - draw_scroll.x; rect_pos.y += g.FontSize; } } - const int buf_display_len = edit_state.CurLenA; - if (is_multiline || buf_display_len < buf_display_max_length) - draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos - render_scroll, GetColorU32(ImGuiCol_Text), buf_display, buf_display + buf_display_len, 0.0f, is_multiline ? NULL : &clip_rect); + // We test for 'buf_display_max_length' as a way to avoid some pathological cases (e.g. single-line 1 MB string) which would make ImDrawList crash. + if (is_multiline || (buf_display_end - buf_display) < buf_display_max_length) + { + ImU32 col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text); + draw_window->DrawList->AddText(g.Font, g.FontSize, draw_pos - draw_scroll, col, buf_display, buf_display_end, 0.0f, is_multiline ? NULL : &clip_rect); + } // Draw blinking cursor - bool cursor_is_visible = (!g.IO.ConfigInputTextCursorBlink) || (g.InputTextState.CursorAnim <= 0.0f) || ImFmod(g.InputTextState.CursorAnim, 1.20f) <= 0.80f; - ImVec2 cursor_screen_pos = render_pos + cursor_offset - render_scroll; - ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y-g.FontSize+0.5f, cursor_screen_pos.x+1.0f, cursor_screen_pos.y-1.5f); - if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect)) - draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text)); - - // Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.) - if (is_editable) - g.PlatformImePos = ImVec2(cursor_screen_pos.x - 1, cursor_screen_pos.y - g.FontSize); + if (render_cursor) + { + state->CursorAnim += io.DeltaTime; + bool cursor_is_visible = (!g.IO.ConfigInputTextCursorBlink) || (state->CursorAnim <= 0.0f) || ImFmod(state->CursorAnim, 1.20f) <= 0.80f; + ImVec2 cursor_screen_pos = draw_pos + cursor_offset - draw_scroll; + ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y - g.FontSize + 0.5f, cursor_screen_pos.x + 1.0f, cursor_screen_pos.y - 1.5f); + if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect)) + draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text)); + + // Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.) + if (!is_readonly) + g.PlatformImePos = ImVec2(cursor_screen_pos.x - 1.0f, cursor_screen_pos.y - g.FontSize); + } } else { - // Render text only - const char* buf_end = NULL; + // Render text only (no selection, no cursor) if (is_multiline) - text_size = ImVec2(size.x, InputTextCalcTextLenAndLineCount(buf_display, &buf_end) * g.FontSize); // We don't need width - else - buf_end = buf_display + strlen(buf_display); - if (is_multiline || (buf_end - buf_display) < buf_display_max_length) - draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos, GetColorU32(ImGuiCol_Text), buf_display, buf_end, 0.0f, is_multiline ? NULL : &clip_rect); + text_size = ImVec2(size.x, InputTextCalcTextLenAndLineCount(buf_display, &buf_display_end) * g.FontSize); // We don't need width + else if (!is_displaying_hint && g.ActiveId == id) + buf_display_end = buf_display + state->CurLenA; + else if (!is_displaying_hint) + buf_display_end = buf_display + strlen(buf_display); + + if (is_multiline || (buf_display_end - buf_display) < buf_display_max_length) + { + ImU32 col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text); + draw_window->DrawList->AddText(g.Font, g.FontSize, draw_pos, col, buf_display, buf_display_end, 0.0f, is_multiline ? NULL : &clip_rect); + } } if (is_multiline) @@ -3758,12 +3926,12 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 EndGroup(); } - if (is_password) + if (is_password && !is_displaying_hint) PopFont(); // Log as text - if (g.LogEnabled && !is_password) - LogRenderedText(&render_pos, buf_display, NULL); + if (g.LogEnabled && !(is_password && !is_displaying_hint)) + LogRenderedText(&draw_pos, buf_display, buf_display_end); if (label_size.x > 0) RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); @@ -3771,6 +3939,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 if (value_changed) MarkItemEdited(id); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags); if ((flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0) return enter_pressed; else @@ -3819,20 +3988,24 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag // If we're not showing any slider there's no point in doing any HSV conversions const ImGuiColorEditFlags flags_untouched = flags; if (flags & ImGuiColorEditFlags_NoInputs) - flags = (flags & (~ImGuiColorEditFlags__InputsMask)) | ImGuiColorEditFlags_RGB | ImGuiColorEditFlags_NoOptions; + flags = (flags & (~ImGuiColorEditFlags__DisplayMask)) | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_NoOptions; // Context menu: display and modify options (before defaults are applied) if (!(flags & ImGuiColorEditFlags_NoOptions)) ColorEditOptionsPopup(col, flags); // Read stored options - if (!(flags & ImGuiColorEditFlags__InputsMask)) - flags |= (g.ColorEditOptions & ImGuiColorEditFlags__InputsMask); + if (!(flags & ImGuiColorEditFlags__DisplayMask)) + flags |= (g.ColorEditOptions & ImGuiColorEditFlags__DisplayMask); if (!(flags & ImGuiColorEditFlags__DataTypeMask)) flags |= (g.ColorEditOptions & ImGuiColorEditFlags__DataTypeMask); if (!(flags & ImGuiColorEditFlags__PickerMask)) flags |= (g.ColorEditOptions & ImGuiColorEditFlags__PickerMask); - flags |= (g.ColorEditOptions & ~(ImGuiColorEditFlags__InputsMask | ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask)); + if (!(flags & ImGuiColorEditFlags__InputMask)) + flags |= (g.ColorEditOptions & ImGuiColorEditFlags__InputMask); + flags |= (g.ColorEditOptions & ~(ImGuiColorEditFlags__DisplayMask | ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask | ImGuiColorEditFlags__InputMask)); + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__DisplayMask)); // Check that only 1 is selected + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__InputMask)); // Check that only 1 is selected const bool alpha = (flags & ImGuiColorEditFlags_NoAlpha) == 0; const bool hdr = (flags & ImGuiColorEditFlags_HDR) != 0; @@ -3840,34 +4013,36 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag // Convert to the formats we need float f[4] = { col[0], col[1], col[2], alpha ? col[3] : 1.0f }; - if (flags & ImGuiColorEditFlags_HSV) + if ((flags & ImGuiColorEditFlags_InputHSV) && (flags & ImGuiColorEditFlags_DisplayRGB)) + ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]); + else if ((flags & ImGuiColorEditFlags_InputRGB) && (flags & ImGuiColorEditFlags_DisplayHSV)) ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]); int i[4] = { IM_F32_TO_INT8_UNBOUND(f[0]), IM_F32_TO_INT8_UNBOUND(f[1]), IM_F32_TO_INT8_UNBOUND(f[2]), IM_F32_TO_INT8_UNBOUND(f[3]) }; bool value_changed = false; bool value_changed_as_float = false; - if ((flags & (ImGuiColorEditFlags_RGB | ImGuiColorEditFlags_HSV)) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0) + if ((flags & (ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHSV)) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0) { // RGB/HSV 0..255 Sliders const float w_item_one = ImMax(1.0f, (float)(int)((w_items_all - (style.ItemInnerSpacing.x) * (components-1)) / (float)components)); const float w_item_last = ImMax(1.0f, (float)(int)(w_items_all - (w_item_one + style.ItemInnerSpacing.x) * (components-1))); const bool hide_prefix = (w_item_one <= CalcTextSize((flags & ImGuiColorEditFlags_Float) ? "M:0.000" : "M:000").x); - const char* ids[4] = { "##X", "##Y", "##Z", "##W" }; - const char* fmt_table_int[3][4] = + static const char* ids[4] = { "##X", "##Y", "##Z", "##W" }; + static const char* fmt_table_int[3][4] = { { "%3d", "%3d", "%3d", "%3d" }, // Short display { "R:%3d", "G:%3d", "B:%3d", "A:%3d" }, // Long display for RGBA { "H:%3d", "S:%3d", "V:%3d", "A:%3d" } // Long display for HSVA }; - const char* fmt_table_float[3][4] = + static const char* fmt_table_float[3][4] = { { "%0.3f", "%0.3f", "%0.3f", "%0.3f" }, // Short display { "R:%0.3f", "G:%0.3f", "B:%0.3f", "A:%0.3f" }, // Long display for RGBA { "H:%0.3f", "S:%0.3f", "V:%0.3f", "A:%0.3f" } // Long display for HSVA }; - const int fmt_idx = hide_prefix ? 0 : (flags & ImGuiColorEditFlags_HSV) ? 2 : 1; + const int fmt_idx = hide_prefix ? 0 : (flags & ImGuiColorEditFlags_DisplayHSV) ? 2 : 1; PushItemWidth(w_item_one); for (int n = 0; n < components; n++) @@ -3877,16 +4052,21 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag if (n + 1 == components) PushItemWidth(w_item_last); if (flags & ImGuiColorEditFlags_Float) - value_changed = value_changed_as_float = value_changed | DragFloat(ids[n], &f[n], 1.0f/255.0f, 0.0f, hdr ? 0.0f : 1.0f, fmt_table_float[fmt_idx][n]); + { + value_changed |= DragFloat(ids[n], &f[n], 1.0f/255.0f, 0.0f, hdr ? 0.0f : 1.0f, fmt_table_float[fmt_idx][n]); + value_changed_as_float |= value_changed; + } else + { value_changed |= DragInt(ids[n], &i[n], 1.0f, 0, hdr ? 0 : 255, fmt_table_int[fmt_idx][n]); + } if (!(flags & ImGuiColorEditFlags_NoOptions)) OpenPopupOnItemClick("context"); } PopItemWidth(); PopItemWidth(); } - else if ((flags & ImGuiColorEditFlags_HEX) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0) + else if ((flags & ImGuiColorEditFlags_DisplayHex) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0) { // RGB Hexadecimal Input char buf[64]; @@ -3937,11 +4117,11 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag picker_active_window = g.CurrentWindow; if (label != label_display_end) { - TextUnformatted(label, label_display_end); + TextEx(label, label_display_end); Spacing(); } - ImGuiColorEditFlags picker_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaBar; - ImGuiColorEditFlags picker_flags = (flags_untouched & picker_flags_to_forward) | ImGuiColorEditFlags__InputsMask | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaPreviewHalf; + ImGuiColorEditFlags picker_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask | ImGuiColorEditFlags__InputMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaBar; + ImGuiColorEditFlags picker_flags = (flags_untouched & picker_flags_to_forward) | ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaPreviewHalf; PushItemWidth(square_sz * 12.0f); // Use 256 + bar sizes? value_changed |= ColorPicker4("##picker", col, picker_flags, &g.ColorPickerRef.x); PopItemWidth(); @@ -3952,25 +4132,25 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag if (label != label_display_end && !(flags & ImGuiColorEditFlags_NoLabel)) { SameLine(0, style.ItemInnerSpacing.x); - TextUnformatted(label, label_display_end); + TextEx(label, label_display_end); } // Convert back - if (picker_active_window == NULL) + if (value_changed && picker_active_window == NULL) { if (!value_changed_as_float) for (int n = 0; n < 4; n++) f[n] = i[n] / 255.0f; - if (flags & ImGuiColorEditFlags_HSV) + if ((flags & ImGuiColorEditFlags_DisplayHSV) && (flags & ImGuiColorEditFlags_InputRGB)) ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]); - if (value_changed) - { - col[0] = f[0]; - col[1] = f[1]; - col[2] = f[2]; - if (alpha) - col[3] = f[3]; - } + if ((flags & ImGuiColorEditFlags_DisplayRGB) && (flags & ImGuiColorEditFlags_InputHSV)) + ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]); + + col[0] = f[0]; + col[1] = f[1]; + col[2] = f[2]; + if (alpha) + col[3] = f[3]; } PopID(); @@ -3980,16 +4160,21 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag // NB: The flag test is merely an optional micro-optimization, BeginDragDropTarget() does the same test. if ((window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropTarget()) { + bool accepted_drag_drop = false; if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F)) { - memcpy((float*)col, payload->Data, sizeof(float) * 3); - value_changed = true; + memcpy((float*)col, payload->Data, sizeof(float) * 3); // Preserve alpha if any //-V512 + value_changed = accepted_drag_drop = true; } if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F)) { memcpy((float*)col, payload->Data, sizeof(float) * components); - value_changed = true; + value_changed = accepted_drag_drop = true; } + + // Drag-drop payloads are always RGB + if (accepted_drag_drop && (flags & ImGuiColorEditFlags_InputHSV)) + ColorConvertRGBtoHSV(col[0], col[1], col[2], col[0], col[1], col[2]); EndDragDropTarget(); } @@ -4068,13 +4253,16 @@ static void RenderArrowsForVerticalBar(ImDrawList* draw_list, ImVec2 pos, ImVec2 } // Note: ColorPicker4() only accesses 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. +// (In C++ the 'float col[4]' notation for a function argument is equivalent to 'float* col', we only specify a size to facilitate understanding of the code.) // FIXME: we adjust the big color square height based on item width, which may cause a flickering feedback loop (if automatic height makes a vertical scrollbar appears, affecting automatic width..) bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags, const float* ref_col) { ImGuiContext& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); - ImDrawList* draw_list = window->DrawList; + if (window->SkipItems) + return false; + ImDrawList* draw_list = window->DrawList; ImGuiStyle& style = g.Style; ImGuiIO& io = g.IO; @@ -4091,7 +4279,10 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl // Read stored options if (!(flags & ImGuiColorEditFlags__PickerMask)) flags |= ((g.ColorEditOptions & ImGuiColorEditFlags__PickerMask) ? g.ColorEditOptions : ImGuiColorEditFlags__OptionsDefault) & ImGuiColorEditFlags__PickerMask; - IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__PickerMask))); // Check that only 1 is selected + if (!(flags & ImGuiColorEditFlags__InputMask)) + flags |= ((g.ColorEditOptions & ImGuiColorEditFlags__InputMask) ? g.ColorEditOptions : ImGuiColorEditFlags__OptionsDefault) & ImGuiColorEditFlags__InputMask; + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__PickerMask)); // Check that only 1 is selected + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__InputMask)); // Check that only 1 is selected if (!(flags & ImGuiColorEditFlags_NoOptions)) flags |= (g.ColorEditOptions & ImGuiColorEditFlags_AlphaBar); @@ -4120,8 +4311,12 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl ImVec2 triangle_pb = ImVec2(triangle_r * -0.5f, triangle_r * -0.866025f); // Black point. ImVec2 triangle_pc = ImVec2(triangle_r * -0.5f, triangle_r * +0.866025f); // White point. - float H,S,V; - ColorConvertRGBtoHSV(col[0], col[1], col[2], H, S, V); + float H = col[0], S = col[1], V = col[2]; + float R = col[0], G = col[1], B = col[2]; + if (flags & ImGuiColorEditFlags_InputRGB) + ColorConvertRGBtoHSV(R, G, B, H, S, V); + else if (flags & ImGuiColorEditFlags_InputHSV) + ColorConvertHSVtoRGB(H, S, V, R, G, B); bool value_changed = false, value_changed_h = false, value_changed_sv = false; @@ -4210,7 +4405,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl { if ((flags & ImGuiColorEditFlags_NoSidePreview)) SameLine(0, style.ItemInnerSpacing.x); - TextUnformatted(label, label_display_end); + TextEx(label, label_display_end); } } @@ -4220,12 +4415,14 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl ImVec4 col_v4(col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]); if ((flags & ImGuiColorEditFlags_NoLabel)) Text("Current"); - ColorButton("##current", col_v4, (flags & (ImGuiColorEditFlags_HDR|ImGuiColorEditFlags_AlphaPreview|ImGuiColorEditFlags_AlphaPreviewHalf|ImGuiColorEditFlags_NoTooltip)), ImVec2(square_sz * 3, square_sz * 2)); + + ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags__InputMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf | ImGuiColorEditFlags_NoTooltip; + ColorButton("##current", col_v4, (flags & sub_flags_to_forward), ImVec2(square_sz * 3, square_sz * 2)); if (ref_col != NULL) { Text("Original"); ImVec4 ref_col_v4(ref_col[0], ref_col[1], ref_col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : ref_col[3]); - if (ColorButton("##original", ref_col_v4, (flags & (ImGuiColorEditFlags_HDR|ImGuiColorEditFlags_AlphaPreview|ImGuiColorEditFlags_AlphaPreviewHalf|ImGuiColorEditFlags_NoTooltip)), ImVec2(square_sz * 3, square_sz * 2))) + if (ColorButton("##original", ref_col_v4, (flags & sub_flags_to_forward), ImVec2(square_sz * 3, square_sz * 2))) { memcpy(col, ref_col, components * sizeof(float)); value_changed = true; @@ -4237,32 +4434,43 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl // Convert back color to RGB if (value_changed_h || value_changed_sv) - ColorConvertHSVtoRGB(H >= 1.0f ? H - 10 * 1e-6f : H, S > 0.0f ? S : 10*1e-6f, V > 0.0f ? V : 1e-6f, col[0], col[1], col[2]); + { + if (flags & ImGuiColorEditFlags_InputRGB) + { + ColorConvertHSVtoRGB(H >= 1.0f ? H - 10 * 1e-6f : H, S > 0.0f ? S : 10*1e-6f, V > 0.0f ? V : 1e-6f, col[0], col[1], col[2]); + } + else if (flags & ImGuiColorEditFlags_InputHSV) + { + col[0] = H; + col[1] = S; + col[2] = V; + } + } // R,G,B and H,S,V slider color editor bool value_changed_fix_hue_wrap = false; if ((flags & ImGuiColorEditFlags_NoInputs) == 0) { PushItemWidth((alpha_bar ? bar1_pos_x : bar0_pos_x) + bars_width - picker_pos.x); - ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoSmallPreview | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf; + ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__InputMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoSmallPreview | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf; ImGuiColorEditFlags sub_flags = (flags & sub_flags_to_forward) | ImGuiColorEditFlags_NoPicker; - if (flags & ImGuiColorEditFlags_RGB || (flags & ImGuiColorEditFlags__InputsMask) == 0) - if (ColorEdit4("##rgb", col, sub_flags | ImGuiColorEditFlags_RGB)) + if (flags & ImGuiColorEditFlags_DisplayRGB || (flags & ImGuiColorEditFlags__DisplayMask) == 0) + if (ColorEdit4("##rgb", col, sub_flags | ImGuiColorEditFlags_DisplayRGB)) { // FIXME: Hackily differenciating using the DragInt (ActiveId != 0 && !ActiveIdAllowOverlap) vs. using the InputText or DropTarget. // For the later we don't want to run the hue-wrap canceling code. If you are well versed in HSV picker please provide your input! (See #2050) value_changed_fix_hue_wrap = (g.ActiveId != 0 && !g.ActiveIdAllowOverlap); value_changed = true; } - if (flags & ImGuiColorEditFlags_HSV || (flags & ImGuiColorEditFlags__InputsMask) == 0) - value_changed |= ColorEdit4("##hsv", col, sub_flags | ImGuiColorEditFlags_HSV); - if (flags & ImGuiColorEditFlags_HEX || (flags & ImGuiColorEditFlags__InputsMask) == 0) - value_changed |= ColorEdit4("##hex", col, sub_flags | ImGuiColorEditFlags_HEX); + if (flags & ImGuiColorEditFlags_DisplayHSV || (flags & ImGuiColorEditFlags__DisplayMask) == 0) + value_changed |= ColorEdit4("##hsv", col, sub_flags | ImGuiColorEditFlags_DisplayHSV); + if (flags & ImGuiColorEditFlags_DisplayHex || (flags & ImGuiColorEditFlags__DisplayMask) == 0) + value_changed |= ColorEdit4("##hex", col, sub_flags | ImGuiColorEditFlags_DisplayHex); PopItemWidth(); } // Try to cancel hue wrap (after ColorEdit4 call), if any - if (value_changed_fix_hue_wrap) + if (value_changed_fix_hue_wrap && (flags & ImGuiColorEditFlags_InputRGB)) { float new_H, new_S, new_V; ColorConvertRGBtoHSV(col[0], col[1], col[2], new_H, new_S, new_V); @@ -4275,9 +4483,27 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl } } + if (value_changed) + { + if (flags & ImGuiColorEditFlags_InputRGB) + { + R = col[0]; + G = col[1]; + B = col[2]; + ColorConvertRGBtoHSV(R, G, B, H, S, V); + } + else if (flags & ImGuiColorEditFlags_InputHSV) + { + H = col[0]; + S = col[1]; + V = col[2]; + ColorConvertHSVtoRGB(H, S, V, R, G, B); + } + } + ImVec4 hue_color_f(1, 1, 1, 1); ColorConvertHSVtoRGB(H, 1, 1, hue_color_f.x, hue_color_f.y, hue_color_f.z); ImU32 hue_color32 = ColorConvertFloat4ToU32(hue_color_f); - ImU32 col32_no_alpha = ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 1.0f)); + ImU32 col32_no_alpha = ColorConvertFloat4ToU32(ImVec4(R, G, B, 1.0f)); const ImU32 hue_colors[6+1] = { IM_COL32(255,0,0,255), IM_COL32(255,255,0,255), IM_COL32(0,255,0,255), IM_COL32(0,255,255,255), IM_COL32(0,0,255,255), IM_COL32(255,0,255,255), IM_COL32(255,0,0,255) }; ImVec2 sv_cursor_pos; @@ -4377,6 +4603,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl // A little colored square. Return true when clicked. // FIXME: May want to display/ignore the alpha component in the color display? Yet show it in the tooltip. // 'desc_id' is not called 'label' because we don't display it next to the button, but only in the tooltip. +// Note that 'col' may be encoded in HSV if ImGuiColorEditFlags_InputHSV is set. bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags, ImVec2 size) { ImGuiWindow* window = GetCurrentWindow(); @@ -4401,22 +4628,26 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl if (flags & ImGuiColorEditFlags_NoAlpha) flags &= ~(ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf); - ImVec4 col_without_alpha(col.x, col.y, col.z, 1.0f); + ImVec4 col_rgb = col; + if (flags & ImGuiColorEditFlags_InputHSV) + ColorConvertHSVtoRGB(col_rgb.x, col_rgb.y, col_rgb.z, col_rgb.x, col_rgb.y, col_rgb.z); + + ImVec4 col_rgb_without_alpha(col_rgb.x, col_rgb.y, col_rgb.z, 1.0f); float grid_step = ImMin(size.x, size.y) / 2.99f; float rounding = ImMin(g.Style.FrameRounding, grid_step * 0.5f); ImRect bb_inner = bb; float off = -0.75f; // The border (using Col_FrameBg) tends to look off when color is near-opaque and rounding is enabled. This offset seemed like a good middle ground to reduce those artifacts. bb_inner.Expand(off); - if ((flags & ImGuiColorEditFlags_AlphaPreviewHalf) && col.w < 1.0f) + if ((flags & ImGuiColorEditFlags_AlphaPreviewHalf) && col_rgb.w < 1.0f) { float mid_x = (float)(int)((bb_inner.Min.x + bb_inner.Max.x) * 0.5f + 0.5f); - RenderColorRectWithAlphaCheckerboard(ImVec2(bb_inner.Min.x + grid_step, bb_inner.Min.y), bb_inner.Max, GetColorU32(col), grid_step, ImVec2(-grid_step + off, off), rounding, ImDrawCornerFlags_TopRight| ImDrawCornerFlags_BotRight); - window->DrawList->AddRectFilled(bb_inner.Min, ImVec2(mid_x, bb_inner.Max.y), GetColorU32(col_without_alpha), rounding, ImDrawCornerFlags_TopLeft|ImDrawCornerFlags_BotLeft); + RenderColorRectWithAlphaCheckerboard(ImVec2(bb_inner.Min.x + grid_step, bb_inner.Min.y), bb_inner.Max, GetColorU32(col_rgb), grid_step, ImVec2(-grid_step + off, off), rounding, ImDrawCornerFlags_TopRight| ImDrawCornerFlags_BotRight); + window->DrawList->AddRectFilled(bb_inner.Min, ImVec2(mid_x, bb_inner.Max.y), GetColorU32(col_rgb_without_alpha), rounding, ImDrawCornerFlags_TopLeft|ImDrawCornerFlags_BotLeft); } else { // Because GetColorU32() multiplies by the global style Alpha and we don't want to display a checkerboard if the source code had no alpha - ImVec4 col_source = (flags & ImGuiColorEditFlags_AlphaPreview) ? col : col_without_alpha; + ImVec4 col_source = (flags & ImGuiColorEditFlags_AlphaPreview) ? col_rgb : col_rgb_without_alpha; if (col_source.w < 1.0f) RenderColorRectWithAlphaCheckerboard(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), grid_step, ImVec2(off, off), rounding); else @@ -4433,18 +4664,18 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl if (g.ActiveId == id && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropSource()) { if (flags & ImGuiColorEditFlags_NoAlpha) - SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F, &col, sizeof(float) * 3, ImGuiCond_Once); + SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F, &col_rgb, sizeof(float) * 3, ImGuiCond_Once); else - SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F, &col, sizeof(float) * 4, ImGuiCond_Once); + SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F, &col_rgb, sizeof(float) * 4, ImGuiCond_Once); ColorButton(desc_id, col, flags); SameLine(); - TextUnformatted("Color"); + TextEx("Color"); EndDragDropSource(); } // Tooltip if (!(flags & ImGuiColorEditFlags_NoTooltip) && hovered) - ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)); + ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags__InputMask | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)); if (pressed) MarkItemEdited(id); @@ -4452,18 +4683,22 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl return pressed; } +// Initialize/override default color options void ImGui::SetColorEditOptions(ImGuiColorEditFlags flags) { ImGuiContext& g = *GImGui; - if ((flags & ImGuiColorEditFlags__InputsMask) == 0) - flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__InputsMask; + if ((flags & ImGuiColorEditFlags__DisplayMask) == 0) + flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__DisplayMask; if ((flags & ImGuiColorEditFlags__DataTypeMask) == 0) flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__DataTypeMask; if ((flags & ImGuiColorEditFlags__PickerMask) == 0) flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__PickerMask; - IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__InputsMask))); // Check only 1 option is selected - IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__DataTypeMask))); // Check only 1 option is selected - IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__PickerMask))); // Check only 1 option is selected + if ((flags & ImGuiColorEditFlags__InputMask) == 0) + flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__InputMask; + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__DisplayMask)); // Check only 1 option is selected + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__DataTypeMask)); // Check only 1 option is selected + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__PickerMask)); // Check only 1 option is selected + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__InputMask)); // Check only 1 option is selected g.ColorEditOptions = flags; } @@ -4472,39 +4707,49 @@ void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags { ImGuiContext& g = *GImGui; - int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]); BeginTooltipEx(0, true); - const char* text_end = text ? FindRenderedTextEnd(text, NULL) : text; if (text_end > text) { - TextUnformatted(text, text_end); + TextEx(text, text_end); Separator(); } ImVec2 sz(g.FontSize * 3 + g.Style.FramePadding.y * 2, g.FontSize * 3 + g.Style.FramePadding.y * 2); - ColorButton("##preview", ImVec4(col[0], col[1], col[2], col[3]), (flags & (ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)) | ImGuiColorEditFlags_NoTooltip, sz); + ImVec4 cf(col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]); + int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]); + ColorButton("##preview", cf, (flags & (ImGuiColorEditFlags__InputMask | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)) | ImGuiColorEditFlags_NoTooltip, sz); SameLine(); - if (flags & ImGuiColorEditFlags_NoAlpha) - Text("#%02X%02X%02X\nR: %d, G: %d, B: %d\n(%.3f, %.3f, %.3f)", cr, cg, cb, cr, cg, cb, col[0], col[1], col[2]); - else - Text("#%02X%02X%02X%02X\nR:%d, G:%d, B:%d, A:%d\n(%.3f, %.3f, %.3f, %.3f)", cr, cg, cb, ca, cr, cg, cb, ca, col[0], col[1], col[2], col[3]); - EndTooltip(); -} - -void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags) -{ - bool allow_opt_inputs = !(flags & ImGuiColorEditFlags__InputsMask); - bool allow_opt_datatype = !(flags & ImGuiColorEditFlags__DataTypeMask); - if ((!allow_opt_inputs && !allow_opt_datatype) || !BeginPopup("context")) + if ((flags & ImGuiColorEditFlags_InputRGB) || !(flags & ImGuiColorEditFlags__InputMask)) + { + if (flags & ImGuiColorEditFlags_NoAlpha) + Text("#%02X%02X%02X\nR: %d, G: %d, B: %d\n(%.3f, %.3f, %.3f)", cr, cg, cb, cr, cg, cb, col[0], col[1], col[2]); + else + Text("#%02X%02X%02X%02X\nR:%d, G:%d, B:%d, A:%d\n(%.3f, %.3f, %.3f, %.3f)", cr, cg, cb, ca, cr, cg, cb, ca, col[0], col[1], col[2], col[3]); + } + else if (flags & ImGuiColorEditFlags_InputHSV) + { + if (flags & ImGuiColorEditFlags_NoAlpha) + Text("H: %.3f, S: %.3f, V: %.3f", col[0], col[1], col[2]); + else + Text("H: %.3f, S: %.3f, V: %.3f, A: %.3f", col[0], col[1], col[2], col[3]); + } + EndTooltip(); +} + +void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags) +{ + bool allow_opt_inputs = !(flags & ImGuiColorEditFlags__DisplayMask); + bool allow_opt_datatype = !(flags & ImGuiColorEditFlags__DataTypeMask); + if ((!allow_opt_inputs && !allow_opt_datatype) || !BeginPopup("context")) return; ImGuiContext& g = *GImGui; ImGuiColorEditFlags opts = g.ColorEditOptions; if (allow_opt_inputs) { - if (RadioButton("RGB", (opts & ImGuiColorEditFlags_RGB) != 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_RGB; - if (RadioButton("HSV", (opts & ImGuiColorEditFlags_HSV) != 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_HSV; - if (RadioButton("HEX", (opts & ImGuiColorEditFlags_HEX) != 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_HEX; + if (RadioButton("RGB", (opts & ImGuiColorEditFlags_DisplayRGB) != 0)) opts = (opts & ~ImGuiColorEditFlags__DisplayMask) | ImGuiColorEditFlags_DisplayRGB; + if (RadioButton("HSV", (opts & ImGuiColorEditFlags_DisplayHSV) != 0)) opts = (opts & ~ImGuiColorEditFlags__DisplayMask) | ImGuiColorEditFlags_DisplayHSV; + if (RadioButton("Hex", (opts & ImGuiColorEditFlags_DisplayHex) != 0)) opts = (opts & ~ImGuiColorEditFlags__DisplayMask) | ImGuiColorEditFlags_DisplayHex; } if (allow_opt_datatype) { @@ -4564,7 +4809,7 @@ void ImGui::ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags fl g.ColorEditOptions = (g.ColorEditOptions & ~ImGuiColorEditFlags__PickerMask) | (picker_flags & ImGuiColorEditFlags__PickerMask); SetCursorScreenPos(backup_pos); ImVec4 dummy_ref_col; - memcpy(&dummy_ref_col.x, ref_col, sizeof(float) * (picker_flags & ImGuiColorEditFlags_NoAlpha ? 3 : 4)); + memcpy(&dummy_ref_col, ref_col, sizeof(float) * ((picker_flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4)); ColorPicker4("##dummypicker", &dummy_ref_col.x, picker_flags); PopID(); } @@ -4585,7 +4830,7 @@ void ImGui::ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags fl // - TreeNodeV() // - TreeNodeEx() // - TreeNodeExV() -// - TreeNodeBehavior() [Internal] +// - TreeNodeBehavior() [Internal] // - TreePush() // - TreePop() // - TreeAdvanceToLabelPos() @@ -4720,7 +4965,7 @@ bool ImGui::TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags) // When logging is enabled, we automatically expand tree nodes (but *NOT* collapsing headers.. seems like sensible behavior). // NB- If we are above max depth we still allow manually opened nodes to be logged. - if (g.LogEnabled && !(flags & ImGuiTreeNodeFlags_NoAutoOpenOnLog) && window->DC.TreeDepth < g.LogAutoExpandMaxDepth) + if (g.LogEnabled && !(flags & ImGuiTreeNodeFlags_NoAutoOpenOnLog) && (window->DC.TreeDepth - g.LogDepthRef) < g.LogDepthToExpand) is_open = true; return is_open; @@ -4760,6 +5005,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l // (Ideally we'd want to add a flag for the user to specify if we want the hit test to be done up to the right side of the content or not) const ImRect interact_bb = display_frame ? frame_bb : ImRect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + text_width + style.ItemSpacing.x*2, frame_bb.Max.y); bool is_open = TreeNodeBehaviorIsOpen(id, flags); + bool is_leaf = (flags & ImGuiTreeNodeFlags_Leaf) != 0; // Store a flag for the current depth to tell if we will allow closing this node when navigating one of its child. // For this purpose we essentially compare if g.NavIdIsAlive went from 0 to 1 between TreeNode() and TreePop(). @@ -4775,24 +5021,29 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l { if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) TreePushRawID(id); + IMGUI_TEST_ENGINE_ITEM_INFO(window->DC.LastItemId, label, window->DC.ItemFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0)); return is_open; } // Flags that affects opening behavior: - // - 0(default) ..................... single-click anywhere to open + // - 0 (default) .................... single-click anywhere to open // - OpenOnDoubleClick .............. double-click anywhere to open // - OpenOnArrow .................... single-click on arrow to open // - OpenOnDoubleClick|OpenOnArrow .. single-click on arrow or double-click anywhere to open - ImGuiButtonFlags button_flags = ImGuiButtonFlags_NoKeyModifiers | ((flags & ImGuiTreeNodeFlags_AllowItemOverlap) ? ImGuiButtonFlags_AllowItemOverlap : 0); - if (!(flags & ImGuiTreeNodeFlags_Leaf)) - button_flags |= ImGuiButtonFlags_PressedOnDragDropHold; + ImGuiButtonFlags button_flags = ImGuiButtonFlags_NoKeyModifiers; + if (flags & ImGuiTreeNodeFlags_AllowItemOverlap) + button_flags |= ImGuiButtonFlags_AllowItemOverlap; if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) button_flags |= ImGuiButtonFlags_PressedOnDoubleClick | ((flags & ImGuiTreeNodeFlags_OpenOnArrow) ? ImGuiButtonFlags_PressedOnClickRelease : 0); + if (!is_leaf) + button_flags |= ImGuiButtonFlags_PressedOnDragDropHold; - bool hovered, held, pressed = ButtonBehavior(interact_bb, id, &hovered, &held, button_flags); - if (!(flags & ImGuiTreeNodeFlags_Leaf)) + bool selected = (flags & ImGuiTreeNodeFlags_Selected) != 0; + bool hovered, held; + bool pressed = ButtonBehavior(interact_bb, id, &hovered, &held, button_flags); + bool toggled = false; + if (!is_leaf) { - bool toggled = false; if (pressed) { toggled = !(flags & (ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick)) || (g.NavActivateId == id); @@ -4827,11 +5078,12 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l // Render const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); const ImVec2 text_pos = frame_bb.Min + ImVec2(text_offset_x, text_base_offset_y); + ImGuiNavHighlightFlags nav_highlight_flags = ImGuiNavHighlightFlags_TypeThin; if (display_frame) { // Framed type RenderFrame(frame_bb.Min, frame_bb.Max, col, true, style.FrameRounding); - RenderNavHighlight(frame_bb, id, ImGuiNavHighlightFlags_TypeThin); + RenderNavHighlight(frame_bb, id, nav_highlight_flags); RenderArrow(frame_bb.Min + ImVec2(padding.x, text_base_offset_y), is_open ? ImGuiDir_Down : ImGuiDir_Right, 1.0f); if (g.LogEnabled) { @@ -4840,7 +5092,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l const char log_suffix[] = "##"; LogRenderedText(&text_pos, log_prefix, log_prefix+3); RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size); - LogRenderedText(&text_pos, log_suffix+1, log_suffix+3); + LogRenderedText(&text_pos, log_suffix, log_suffix+2); } else { @@ -4850,15 +5102,15 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l else { // Unframed typed for tree nodes - if (hovered || (flags & ImGuiTreeNodeFlags_Selected)) + if (hovered || selected) { RenderFrame(frame_bb.Min, frame_bb.Max, col, false); - RenderNavHighlight(frame_bb, id, ImGuiNavHighlightFlags_TypeThin); + RenderNavHighlight(frame_bb, id, nav_highlight_flags); } if (flags & ImGuiTreeNodeFlags_Bullet) RenderBullet(frame_bb.Min + ImVec2(text_offset_x * 0.5f, g.FontSize*0.50f + text_base_offset_y)); - else if (!(flags & ImGuiTreeNodeFlags_Leaf)) + else if (!is_leaf) RenderArrow(frame_bb.Min + ImVec2(padding.x, g.FontSize*0.15f + text_base_offset_y), is_open ? ImGuiDir_Down : ImGuiDir_Right, 0.70f); if (g.LogEnabled) LogRenderedText(&text_pos, ">"); @@ -4867,6 +5119,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) TreePushRawID(id); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0)); return is_open; } @@ -4964,7 +5217,7 @@ bool ImGui::CollapsingHeader(const char* label, bool* p_open, ImGuiTreeNodeFlags ImGuiItemHoveredDataBackup last_item_backup; float button_radius = g.FontSize * 0.5f; ImVec2 button_center = ImVec2(ImMin(window->DC.LastItemRect.Max.x, window->ClipRect.Max.x) - g.Style.FramePadding.x - button_radius, window->DC.LastItemRect.GetCenter().y); - if (CloseButton(window->GetID((void*)(intptr_t)(id+1)), button_center, button_radius)) + if (CloseButton(window->GetID((void*)((intptr_t)id+1)), button_center, button_radius)) *p_open = false; last_item_backup.Restore(); } @@ -5003,7 +5256,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl // Fill horizontal space. ImVec2 window_padding = window->WindowPadding; float max_x = (flags & ImGuiSelectableFlags_SpanAllColumns) ? GetWindowContentRegionMax().x : GetContentRegionMax().x; - float w_draw = ImMax(label_size.x, window->Pos.x + max_x - window_padding.x - window->DC.CursorPos.x); + float w_draw = ImMax(label_size.x, window->Pos.x + max_x - window_padding.x - pos.x); ImVec2 size_draw((size_arg.x != 0 && !(flags & ImGuiSelectableFlags_DrawFillAvailWidth)) ? size_arg.x : w_draw, size_arg.y != 0.0f ? size_arg.y : size.y); ImRect bb(pos, pos + size_draw); if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_DrawFillAvailWidth)) @@ -5018,7 +5271,20 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl bb.Min.y -= spacing_U; bb.Max.x += spacing_R; bb.Max.y += spacing_D; - if (!ItemAdd(bb, (flags & ImGuiSelectableFlags_Disabled) ? 0 : id)) + + bool item_add; + if (flags & ImGuiSelectableFlags_Disabled) + { + ImGuiItemFlags backup_item_flags = window->DC.ItemFlags; + window->DC.ItemFlags |= ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNavDefaultFocus; + item_add = ItemAdd(bb, id); + window->DC.ItemFlags = backup_item_flags; + } + else + { + item_add = ItemAdd(bb, id); + } + if (!item_add) { if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet) PushColumnClipRect(); @@ -5032,11 +5298,11 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl if (flags & ImGuiSelectableFlags_PressedOnRelease) button_flags |= ImGuiButtonFlags_PressedOnRelease; if (flags & ImGuiSelectableFlags_Disabled) button_flags |= ImGuiButtonFlags_Disabled; if (flags & ImGuiSelectableFlags_AllowDoubleClick) button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; - bool hovered, held; - bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); if (flags & ImGuiSelectableFlags_Disabled) selected = false; + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); // Hovering selectable with mouse updates NavId accordingly so navigation can be resumed with gamepad/keyboard (this doesn't happen on most widgets) if (pressed || hovered) if (!g.NavDisableMouseHover && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent) @@ -5062,7 +5328,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl } if (flags & ImGuiSelectableFlags_Disabled) PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]); - RenderTextClipped(bb_inner.Min, bb.Max, label, NULL, &label_size, ImVec2(0.0f,0.0f)); + RenderTextClipped(bb_inner.Min, bb_inner.Max, label, NULL, &label_size, style.SelectableTextAlign, &bb); if (flags & ImGuiSelectableFlags_Disabled) PopStyleColor(); // Automatically close popups @@ -5109,6 +5375,13 @@ bool ImGui::ListBoxHeader(const char* label, const ImVec2& size_arg) ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); window->DC.LastItemRect = bb; // Forward storage for ListBoxFooter.. dodgy. + if (!IsRectVisible(bb.Min, bb.Max)) + { + ItemSize(bb.GetSize(), style.FramePadding.y); + ItemAdd(bb, 0, &frame_bb); + return false; + } + BeginGroup(); if (label_size.x > 0) RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); @@ -5201,7 +5474,7 @@ bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(v // - PlotHistogram() //------------------------------------------------------------------------- -void ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size) +void ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 frame_size) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) @@ -5209,20 +5482,21 @@ void ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_ge ImGuiContext& g = *GImGui; const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); const ImVec2 label_size = CalcTextSize(label, NULL, true); - if (graph_size.x == 0.0f) - graph_size.x = CalcItemWidth(); - if (graph_size.y == 0.0f) - graph_size.y = label_size.y + (style.FramePadding.y * 2); + if (frame_size.x == 0.0f) + frame_size.x = CalcItemWidth(); + if (frame_size.y == 0.0f) + frame_size.y = label_size.y + (style.FramePadding.y * 2); - const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(graph_size.x, graph_size.y)); + const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size); const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding); const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0)); ItemSize(total_bb, style.FramePadding.y); if (!ItemAdd(total_bb, 0, &frame_bb)) return; - const bool hovered = ItemHoverable(inner_bb, 0); + const bool hovered = ItemHoverable(frame_bb, id); // Determine scale from values if not specified if (scale_min == FLT_MAX || scale_max == FLT_MAX) @@ -5243,14 +5517,15 @@ void ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_ge RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); - if (values_count > 0) + const int values_count_min = (plot_type == ImGuiPlotType_Lines) ? 2 : 1; + if (values_count >= values_count_min) { - int res_w = ImMin((int)graph_size.x, values_count) + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0); + int res_w = ImMin((int)frame_size.x, values_count) + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0); int item_count = values_count + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0); // Tooltip on hover int v_hovered = -1; - if (hovered) + if (hovered && inner_bb.Contains(g.IO.MousePos)) { const float t = ImClamp((g.IO.MousePos.x - inner_bb.Min.x) / (inner_bb.Max.x - inner_bb.Min.x), 0.0f, 0.9999f); const int v_idx = (int)(t * item_count); @@ -5400,7 +5675,6 @@ void ImGui::Value(const char* prefix, float v, const char* float_format) // Helpers for internal use ImGuiMenuColumns::ImGuiMenuColumns() { - Count = 0; Spacing = Width = NextWidth = 0.0f; memset(Pos, 0, sizeof(Pos)); memset(NextWidths, 0, sizeof(NextWidths)); @@ -5408,12 +5682,13 @@ ImGuiMenuColumns::ImGuiMenuColumns() void ImGuiMenuColumns::Update(int count, float spacing, bool clear) { - IM_ASSERT(Count <= IM_ARRAYSIZE(Pos)); - Count = count; + IM_ASSERT(count == IM_ARRAYSIZE(Pos)); + (void)count; // [Bruno Levy] silence a warning Width = NextWidth = 0.0f; Spacing = spacing; - if (clear) memset(NextWidths, 0, sizeof(NextWidths)); - for (int i = 0; i < Count; i++) + if (clear) + memset(NextWidths, 0, sizeof(NextWidths)); + for (int i = 0; i < IM_ARRAYSIZE(Pos); i++) { if (i > 0 && NextWidths[i] > 0.0f) Width += Spacing; @@ -5429,7 +5704,7 @@ float ImGuiMenuColumns::DeclColumns(float w0, float w1, float w2) // not using v NextWidths[0] = ImMax(NextWidths[0], w0); NextWidths[1] = ImMax(NextWidths[1], w1); NextWidths[2] = ImMax(NextWidths[2], w2); - for (int i = 0; i < 3; i++) + for (int i = 0; i < IM_ARRAYSIZE(Pos); i++) NextWidth += NextWidths[i] + ((i > 0 && NextWidths[i] > 0.0f) ? Spacing : 0.0f); return ImMax(Width, NextWidth); } @@ -5457,7 +5732,7 @@ bool ImGui::BeginMainMenuBar() End(); return false; } - return true; + return true; //-V1020 } void ImGui::EndMainMenuBar() @@ -5493,8 +5768,8 @@ bool ImGui::BeginMenuBar() window->DC.CursorPos = ImVec2(bar_rect.Min.x + window->DC.MenuBarOffset.x, bar_rect.Min.y + window->DC.MenuBarOffset.y); window->DC.LayoutType = ImGuiLayoutType_Horizontal; - window->DC.NavLayerCurrent++; - window->DC.NavLayerCurrentMask <<= 1; + window->DC.NavLayerCurrent = ImGuiNavLayer_Menu; + window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Menu); window->DC.MenuBarAppending = true; AlignTextToFramePadding(); return true; @@ -5520,7 +5795,7 @@ void ImGui::EndMenuBar() IM_ASSERT(window->DC.NavLayerActiveMaskNext & 0x02); // Sanity check FocusWindow(window); SetNavIDWithRectRel(window->NavLastIds[1], 1, window->NavRectRel[1]); - g.NavLayer = 1; + g.NavLayer = ImGuiNavLayer_Menu; g.NavDisableHighlight = true; // Hide highlight for the current frame so we don't see the intermediary selection. g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued; NavMoveRequestCancel(); @@ -5535,8 +5810,8 @@ void ImGui::EndMenuBar() window->DC.GroupStack.back().AdvanceCursor = false; EndGroup(); // Restore position on layer 0 window->DC.LayoutType = ImGuiLayoutType_Vertical; - window->DC.NavLayerCurrent--; - window->DC.NavLayerCurrentMask >>= 1; + window->DC.NavLayerCurrent = ImGuiNavLayer_Main; + window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main); window->DC.MenuBarAppending = false; } @@ -5554,12 +5829,14 @@ bool ImGui::BeginMenu(const char* label, bool enabled) bool pressed; bool menu_is_open = IsPopupOpen(id); - bool menuset_is_open = !(window->Flags & ImGuiWindowFlags_Popup) && (g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].OpenParentId == window->IDStack.back()); + bool menuset_is_open = !(window->Flags & ImGuiWindowFlags_Popup) && (g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].OpenParentId == window->IDStack.back()); ImGuiWindow* backed_nav_window = g.NavWindow; if (menuset_is_open) g.NavWindow = window; // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent) - // The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu (using FindBestWindowPosForPopup). + // The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu, + // However the final position is going to be different! It is choosen by FindBestWindowPosForPopup(). + // e.g. Menus tend to overlap each other horizontally to amplify relative Z-ordering. ImVec2 popup_pos, pos = window->DC.CursorPos; if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) { @@ -5595,17 +5872,18 @@ bool ImGui::BeginMenu(const char* label, bool enabled) { // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive. bool moving_within_opened_triangle = false; - if (g.HoveredWindow == window && g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].ParentWindow == window && !(window->Flags & ImGuiWindowFlags_MenuBar)) + if (g.HoveredWindow == window && g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].ParentWindow == window && !(window->Flags & ImGuiWindowFlags_MenuBar)) { - if (ImGuiWindow* next_window = g.OpenPopupStack[g.CurrentPopupStack.Size].Window) + if (ImGuiWindow* next_window = g.OpenPopupStack[g.BeginPopupStack.Size].Window) { + // FIXME-DPI: Values should be derived from a master "scale" factor. ImRect next_window_rect = next_window->Rect(); ImVec2 ta = g.IO.MousePos - g.IO.MouseDelta; ImVec2 tb = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetTL() : next_window_rect.GetTR(); ImVec2 tc = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR(); float extra = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, 5.0f, 30.0f); // add a bit of extra slack. - ta.x += (window->Pos.x < next_window->Pos.x) ? -0.5f : +0.5f; // to avoid numerical issues - tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -100.0f); // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus // FIXME: Multiply by fb_scale? + ta.x += (window->Pos.x < next_window->Pos.x) ? -0.5f : +0.5f; // to avoid numerical issues + tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -100.0f); // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus // FIXME: Multiply by fb_scale? tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +100.0f); moving_within_opened_triangle = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos); //window->DrawList->PushClipRectFullScreen(); window->DrawList->AddTriangleFilled(ta, tb, tc, moving_within_opened_triangle ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); window->DrawList->PopClipRect(); // Debug @@ -5648,9 +5926,11 @@ bool ImGui::BeginMenu(const char* label, bool enabled) if (!enabled) // explicitly close if an open menu becomes disabled, facilitate users code a lot in pattern such as 'if (BeginMenu("options", has_object)) { ..use object.. }' want_close = true; if (want_close && IsPopupOpen(id)) - ClosePopupToLevel(g.CurrentPopupStack.Size); + ClosePopupToLevel(g.BeginPopupStack.Size, true); - if (!menu_is_open && want_open && g.OpenPopupStack.Size > g.CurrentPopupStack.Size) + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags | ImGuiItemStatusFlags_Openable | (menu_is_open ? ImGuiItemStatusFlags_Opened : 0)); + + if (!menu_is_open && want_open && g.OpenPopupStack.Size > g.BeginPopupStack.Size) { // Don't recycle same menu level in the same frame, first close the other menu and yield for a frame. OpenPopup(label); @@ -5676,14 +5956,14 @@ bool ImGui::BeginMenu(const char* label, bool enabled) void ImGui::EndMenu() { - // Nav: When a left move request _within our child menu_ failed, close the menu. + // Nav: When a left move request _within our child menu_ failed, close ourselves (the _parent_ menu). // A menu doesn't close itself because EndMenuBar() wants the catch the last Left<>Right inputs. // However, it means that with the current code, a BeginMenu() from outside another menu or a menu-bar won't be closable with the Left direction. ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; if (g.NavWindow && g.NavWindow->ParentWindow == window && g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet() && window->DC.LayoutType == ImGuiLayoutType_Vertical) { - ClosePopupToLevel(g.OpenPopupStack.Size - 1); + ClosePopupToLevel(g.BeginPopupStack.Size, true); NavMoveRequestCancel(); } @@ -5729,6 +6009,8 @@ bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, boo if (selected) RenderCheckMark(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled), g.FontSize * 0.866f); } + + IMGUI_TEST_ENGINE_ITEM_INFO(window->DC.LastItemId, label, window->DC.ItemFlags | ImGuiItemStatusFlags_Checkable | (selected ? ImGuiItemStatusFlags_Checked : 0)); return pressed; } @@ -5742,3 +6024,913 @@ bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected, } return false; } + +//------------------------------------------------------------------------- +// [SECTION] Widgets: BeginTabBar, EndTabBar, etc. +//------------------------------------------------------------------------- +// [BETA API] API may evolve! This code has been extracted out of the Docking branch, +// and some of the construct which are not used in Master may be left here to facilitate merging. +//------------------------------------------------------------------------- +// - BeginTabBar() +// - BeginTabBarEx() [Internal] +// - EndTabBar() +// - TabBarLayout() [Internal] +// - TabBarCalcTabID() [Internal] +// - TabBarCalcMaxTabWidth() [Internal] +// - TabBarFindTabById() [Internal] +// - TabBarRemoveTab() [Internal] +// - TabBarCloseTab() [Internal] +// - TabBarScrollClamp()v +// - TabBarScrollToTab() [Internal] +// - TabBarQueueChangeTabOrder() [Internal] +// - TabBarScrollingButtons() [Internal] +// - TabBarTabListPopupButton() [Internal] +//------------------------------------------------------------------------- + +namespace ImGui +{ + static void TabBarLayout(ImGuiTabBar* tab_bar); + static ImU32 TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label); + static float TabBarCalcMaxTabWidth(); + static float TabBarScrollClamp(ImGuiTabBar* tab_bar, float scrolling); + static void TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab); + static ImGuiTabItem* TabBarScrollingButtons(ImGuiTabBar* tab_bar); + static ImGuiTabItem* TabBarTabListPopupButton(ImGuiTabBar* tab_bar); +} + +ImGuiTabBar::ImGuiTabBar() +{ + ID = 0; + SelectedTabId = NextSelectedTabId = VisibleTabId = 0; + CurrFrameVisible = PrevFrameVisible = -1; + ContentsHeight = 0.0f; + OffsetMax = OffsetNextTab = 0.0f; + ScrollingAnim = ScrollingTarget = ScrollingTargetDistToVisibility = ScrollingSpeed = 0.0f; + Flags = ImGuiTabBarFlags_None; + ReorderRequestTabId = 0; + ReorderRequestDir = 0; + WantLayout = VisibleTabWasSubmitted = false; + LastTabItemIdx = -1; +} + +static int IMGUI_CDECL TabItemComparerByVisibleOffset(const void* lhs, const void* rhs) +{ + const ImGuiTabItem* a = (const ImGuiTabItem*)lhs; + const ImGuiTabItem* b = (const ImGuiTabItem*)rhs; + return (int)(a->Offset - b->Offset); +} + +static int IMGUI_CDECL TabBarSortItemComparer(const void* lhs, const void* rhs) +{ + const ImGuiTabBarSortItem* a = (const ImGuiTabBarSortItem*)lhs; + const ImGuiTabBarSortItem* b = (const ImGuiTabBarSortItem*)rhs; + if (int d = (int)(b->Width - a->Width)) + return d; + return (b->Index - a->Index); +} + +static ImGuiTabBar* GetTabBarFromTabBarRef(const ImGuiTabBarRef& ref) +{ + ImGuiContext& g = *GImGui; + return ref.Ptr ? ref.Ptr : g.TabBars.GetByIndex(ref.IndexInMainPool); +} + +static ImGuiTabBarRef GetTabBarRefFromTabBar(ImGuiTabBar* tab_bar) +{ + ImGuiContext& g = *GImGui; + if (g.TabBars.Contains(tab_bar)) + return ImGuiTabBarRef(g.TabBars.GetIndex(tab_bar)); + return ImGuiTabBarRef(tab_bar); +} + +bool ImGui::BeginTabBar(const char* str_id, ImGuiTabBarFlags flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (window->SkipItems) + return false; + + ImGuiID id = window->GetID(str_id); + ImGuiTabBar* tab_bar = g.TabBars.GetOrAddByKey(id); + ImRect tab_bar_bb = ImRect(window->DC.CursorPos.x, window->DC.CursorPos.y, window->InnerClipRect.Max.x, window->DC.CursorPos.y + g.FontSize + g.Style.FramePadding.y * 2); + tab_bar->ID = id; + return BeginTabBarEx(tab_bar, tab_bar_bb, flags | ImGuiTabBarFlags_IsFocused); +} + +bool ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImGuiTabBarFlags flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (window->SkipItems) + return false; + + if ((flags & ImGuiTabBarFlags_DockNode) == 0) + window->IDStack.push_back(tab_bar->ID); + + // Add to stack + g.CurrentTabBarStack.push_back(GetTabBarRefFromTabBar(tab_bar)); + g.CurrentTabBar = tab_bar; + + if (tab_bar->CurrFrameVisible == g.FrameCount) + { + //IMGUI_DEBUG_LOG("BeginTabBarEx already called this frame\n", g.FrameCount); + IM_ASSERT(0); + return true; + } + + // When toggling back from ordered to manually-reorderable, shuffle tabs to enforce the last visible order. + // Otherwise, the most recently inserted tabs would move at the end of visible list which can be a little too confusing or magic for the user. + if ((flags & ImGuiTabBarFlags_Reorderable) && !(tab_bar->Flags & ImGuiTabBarFlags_Reorderable) && tab_bar->Tabs.Size > 1 && tab_bar->PrevFrameVisible != -1) + ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerByVisibleOffset); + + // Flags + if ((flags & ImGuiTabBarFlags_FittingPolicyMask_) == 0) + flags |= ImGuiTabBarFlags_FittingPolicyDefault_; + + tab_bar->Flags = flags; + tab_bar->BarRect = tab_bar_bb; + tab_bar->WantLayout = true; // Layout will be done on the first call to ItemTab() + tab_bar->PrevFrameVisible = tab_bar->CurrFrameVisible; + tab_bar->CurrFrameVisible = g.FrameCount; + tab_bar->FramePadding = g.Style.FramePadding; + + // Layout + ItemSize(ImVec2(tab_bar->OffsetMax, tab_bar->BarRect.GetHeight())); + window->DC.CursorPos.x = tab_bar->BarRect.Min.x; + + // Draw separator + const ImU32 col = GetColorU32((flags & ImGuiTabBarFlags_IsFocused) ? ImGuiCol_TabActive : ImGuiCol_Tab); + const float y = tab_bar->BarRect.Max.y - 1.0f; + { + const float separator_min_x = tab_bar->BarRect.Min.x - window->WindowPadding.x; + const float separator_max_x = tab_bar->BarRect.Max.x + window->WindowPadding.x; + window->DrawList->AddLine(ImVec2(separator_min_x, y), ImVec2(separator_max_x, y), col, 1.0f); + } + return true; +} + +void ImGui::EndTabBar() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (window->SkipItems) + return; + + ImGuiTabBar* tab_bar = g.CurrentTabBar; + if (tab_bar == NULL) + { + IM_ASSERT(tab_bar != NULL && "Mismatched BeginTabBar()/EndTabBar()!"); + return; // FIXME-ERRORHANDLING + } + if (tab_bar->WantLayout) + TabBarLayout(tab_bar); + + // Restore the last visible height if no tab is visible, this reduce vertical flicker/movement when a tabs gets removed without calling SetTabItemClosed(). + const bool tab_bar_appearing = (tab_bar->PrevFrameVisible + 1 < g.FrameCount); + if (tab_bar->VisibleTabWasSubmitted || tab_bar->VisibleTabId == 0 || tab_bar_appearing) + tab_bar->ContentsHeight = ImMax(window->DC.CursorPos.y - tab_bar->BarRect.Max.y, 0.0f); + else + window->DC.CursorPos.y = tab_bar->BarRect.Max.y + tab_bar->ContentsHeight; + + if ((tab_bar->Flags & ImGuiTabBarFlags_DockNode) == 0) + PopID(); + + g.CurrentTabBarStack.pop_back(); + g.CurrentTabBar = g.CurrentTabBarStack.empty() ? NULL : GetTabBarFromTabBarRef(g.CurrentTabBarStack.back()); +} + +// This is called only once a frame before by the first call to ItemTab() +// The reason we're not calling it in BeginTabBar() is to leave a chance to the user to call the SetTabItemClosed() functions. +static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) +{ + ImGuiContext& g = *GImGui; + tab_bar->WantLayout = false; + + // Garbage collect + int tab_dst_n = 0; + for (int tab_src_n = 0; tab_src_n < tab_bar->Tabs.Size; tab_src_n++) + { + ImGuiTabItem* tab = &tab_bar->Tabs[tab_src_n]; + if (tab->LastFrameVisible < tab_bar->PrevFrameVisible) + { + if (tab->ID == tab_bar->SelectedTabId) + tab_bar->SelectedTabId = 0; + continue; + } + if (tab_dst_n != tab_src_n) + tab_bar->Tabs[tab_dst_n] = tab_bar->Tabs[tab_src_n]; + tab_dst_n++; + } + if (tab_bar->Tabs.Size != tab_dst_n) + tab_bar->Tabs.resize(tab_dst_n); + + // Setup next selected tab + ImGuiID scroll_track_selected_tab_id = 0; + if (tab_bar->NextSelectedTabId) + { + tab_bar->SelectedTabId = tab_bar->NextSelectedTabId; + tab_bar->NextSelectedTabId = 0; + scroll_track_selected_tab_id = tab_bar->SelectedTabId; + } + + // Process order change request (we could probably process it when requested but it's just saner to do it in a single spot). + if (tab_bar->ReorderRequestTabId != 0) + { + if (ImGuiTabItem* tab1 = TabBarFindTabByID(tab_bar, tab_bar->ReorderRequestTabId)) + { + //IM_ASSERT(tab_bar->Flags & ImGuiTabBarFlags_Reorderable); // <- this may happen when using debug tools + int tab2_order = tab_bar->GetTabOrder(tab1) + tab_bar->ReorderRequestDir; + if (tab2_order >= 0 && tab2_order < tab_bar->Tabs.Size) + { + ImGuiTabItem* tab2 = &tab_bar->Tabs[tab2_order]; + ImGuiTabItem item_tmp = *tab1; + *tab1 = *tab2; + *tab2 = item_tmp; + if (tab2->ID == tab_bar->SelectedTabId) + scroll_track_selected_tab_id = tab2->ID; + tab1 = tab2 = NULL; + } + if (tab_bar->Flags & ImGuiTabBarFlags_SaveSettings) + MarkIniSettingsDirty(); + } + tab_bar->ReorderRequestTabId = 0; + } + + // Tab List Popup (will alter tab_bar->BarRect and therefore the available width!) + const bool tab_list_popup_button = (tab_bar->Flags & ImGuiTabBarFlags_TabListPopupButton) != 0; + if (tab_list_popup_button) + if (ImGuiTabItem* tab_to_select = TabBarTabListPopupButton(tab_bar)) // NB: Will alter BarRect.Max.x! + scroll_track_selected_tab_id = tab_bar->SelectedTabId = tab_to_select->ID; + + ImVector& width_sort_buffer = g.TabSortByWidthBuffer; + width_sort_buffer.resize(tab_bar->Tabs.Size); + + // Compute ideal widths + float width_total_contents = 0.0f; + ImGuiTabItem* most_recently_selected_tab = NULL; + bool found_selected_tab_id = false; + for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) + { + ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; + IM_ASSERT(tab->LastFrameVisible >= tab_bar->PrevFrameVisible); + + if (most_recently_selected_tab == NULL || most_recently_selected_tab->LastFrameSelected < tab->LastFrameSelected) + most_recently_selected_tab = tab; + if (tab->ID == tab_bar->SelectedTabId) + found_selected_tab_id = true; + + // Refresh tab width immediately, otherwise changes of style e.g. style.FramePadding.x would noticeably lag in the tab bar. + // Additionally, when using TabBarAddTab() to manipulate tab bar order we occasionally insert new tabs that don't have a width yet, + // and we cannot wait for the next BeginTabItem() call. We cannot compute this width within TabBarAddTab() because font size depends on the active window. + const char* tab_name = tab_bar->GetTabName(tab); + tab->WidthContents = TabItemCalcSize(tab_name, (tab->Flags & ImGuiTabItemFlags_NoCloseButton) ? false : true).x; + + width_total_contents += (tab_n > 0 ? g.Style.ItemInnerSpacing.x : 0.0f) + tab->WidthContents; + + // Store data so we can build an array sorted by width if we need to shrink tabs down + width_sort_buffer[tab_n].Index = tab_n; + width_sort_buffer[tab_n].Width = tab->WidthContents; + } + + // Compute width + const float width_avail = tab_bar->BarRect.GetWidth(); + float width_excess = (width_avail < width_total_contents) ? (width_total_contents - width_avail) : 0.0f; + if (width_excess > 0.0f && (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyResizeDown)) + { + // If we don't have enough room, resize down the largest tabs first + if (tab_bar->Tabs.Size > 1) + ImQsort(width_sort_buffer.Data, (size_t)width_sort_buffer.Size, sizeof(ImGuiTabBarSortItem), TabBarSortItemComparer); + int tab_count_same_width = 1; + while (width_excess > 0.0f && tab_count_same_width < tab_bar->Tabs.Size) + { + while (tab_count_same_width < tab_bar->Tabs.Size && width_sort_buffer[0].Width == width_sort_buffer[tab_count_same_width].Width) + tab_count_same_width++; + float width_to_remove_per_tab_max = (tab_count_same_width < tab_bar->Tabs.Size) ? (width_sort_buffer[0].Width - width_sort_buffer[tab_count_same_width].Width) : (width_sort_buffer[0].Width - 1.0f); + float width_to_remove_per_tab = ImMin(width_excess / tab_count_same_width, width_to_remove_per_tab_max); + for (int tab_n = 0; tab_n < tab_count_same_width; tab_n++) + width_sort_buffer[tab_n].Width -= width_to_remove_per_tab; + width_excess -= width_to_remove_per_tab * tab_count_same_width; + } + for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) + tab_bar->Tabs[width_sort_buffer[tab_n].Index].Width = (float)(int)width_sort_buffer[tab_n].Width; + } + else + { + const float tab_max_width = TabBarCalcMaxTabWidth(); + for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) + { + ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; + tab->Width = ImMin(tab->WidthContents, tab_max_width); + } + } + + // Layout all active tabs + float offset_x = 0.0f; + for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) + { + ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; + tab->Offset = offset_x; + if (scroll_track_selected_tab_id == 0 && g.NavJustMovedToId == tab->ID) + scroll_track_selected_tab_id = tab->ID; + offset_x += tab->Width + g.Style.ItemInnerSpacing.x; + } + tab_bar->OffsetMax = ImMax(offset_x - g.Style.ItemInnerSpacing.x, 0.0f); + tab_bar->OffsetNextTab = 0.0f; + + // Horizontal scrolling buttons + const bool scrolling_buttons = (tab_bar->OffsetMax > tab_bar->BarRect.GetWidth() && tab_bar->Tabs.Size > 1) && !(tab_bar->Flags & ImGuiTabBarFlags_NoTabListScrollingButtons) && (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyScroll); + if (scrolling_buttons) + if (ImGuiTabItem* tab_to_select = TabBarScrollingButtons(tab_bar)) // NB: Will alter BarRect.Max.x! + scroll_track_selected_tab_id = tab_bar->SelectedTabId = tab_to_select->ID; + + // If we have lost the selected tab, select the next most recently active one + if (found_selected_tab_id == false) + tab_bar->SelectedTabId = 0; + if (tab_bar->SelectedTabId == 0 && tab_bar->NextSelectedTabId == 0 && most_recently_selected_tab != NULL) + scroll_track_selected_tab_id = tab_bar->SelectedTabId = most_recently_selected_tab->ID; + + // Lock in visible tab + tab_bar->VisibleTabId = tab_bar->SelectedTabId; + tab_bar->VisibleTabWasSubmitted = false; + + // Update scrolling + if (scroll_track_selected_tab_id) + if (ImGuiTabItem* scroll_track_selected_tab = TabBarFindTabByID(tab_bar, scroll_track_selected_tab_id)) + TabBarScrollToTab(tab_bar, scroll_track_selected_tab); + tab_bar->ScrollingAnim = TabBarScrollClamp(tab_bar, tab_bar->ScrollingAnim); + tab_bar->ScrollingTarget = TabBarScrollClamp(tab_bar, tab_bar->ScrollingTarget); + if (tab_bar->ScrollingAnim != tab_bar->ScrollingTarget) + { + // Scrolling speed adjust itself so we can always reach our target in 1/3 seconds. + // Teleport if we are aiming far off the visible line + tab_bar->ScrollingSpeed = ImMax(tab_bar->ScrollingSpeed, 70.0f * g.FontSize); + tab_bar->ScrollingSpeed = ImMax(tab_bar->ScrollingSpeed, ImFabs(tab_bar->ScrollingTarget - tab_bar->ScrollingAnim) / 0.3f); + const bool teleport = (tab_bar->PrevFrameVisible + 1 < g.FrameCount) || (tab_bar->ScrollingTargetDistToVisibility > 10.0f * g.FontSize); + tab_bar->ScrollingAnim = teleport ? tab_bar->ScrollingTarget : ImLinearSweep(tab_bar->ScrollingAnim, tab_bar->ScrollingTarget, g.IO.DeltaTime * tab_bar->ScrollingSpeed); + } + else + { + tab_bar->ScrollingSpeed = 0.0f; + } + + // Clear name buffers + if ((tab_bar->Flags & ImGuiTabBarFlags_DockNode) == 0) + tab_bar->TabsNames.Buf.resize(0); +} + +// Dockables uses Name/ID in the global namespace. Non-dockable items use the ID stack. +static ImU32 ImGui::TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label) +{ + if (tab_bar->Flags & ImGuiTabBarFlags_DockNode) + { + ImGuiID id = ImHashStr(label, 0); + KeepAliveID(id); + return id; + } + else + { + ImGuiWindow* window = GImGui->CurrentWindow; + return window->GetID(label); + } +} + +static float ImGui::TabBarCalcMaxTabWidth() +{ + ImGuiContext& g = *GImGui; + return g.FontSize * 20.0f; +} + +ImGuiTabItem* ImGui::TabBarFindTabByID(ImGuiTabBar* tab_bar, ImGuiID tab_id) +{ + if (tab_id != 0) + for (int n = 0; n < tab_bar->Tabs.Size; n++) + if (tab_bar->Tabs[n].ID == tab_id) + return &tab_bar->Tabs[n]; + return NULL; +} + +// The *TabId fields be already set by the docking system _before_ the actual TabItem was created, so we clear them regardless. +void ImGui::TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id) +{ + if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id)) + tab_bar->Tabs.erase(tab); + if (tab_bar->VisibleTabId == tab_id) { tab_bar->VisibleTabId = 0; } + if (tab_bar->SelectedTabId == tab_id) { tab_bar->SelectedTabId = 0; } + if (tab_bar->NextSelectedTabId == tab_id) { tab_bar->NextSelectedTabId = 0; } +} + +// Called on manual closure attempt +void ImGui::TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab) +{ + if ((tab_bar->VisibleTabId == tab->ID) && !(tab->Flags & ImGuiTabItemFlags_UnsavedDocument)) + { + // This will remove a frame of lag for selecting another tab on closure. + // However we don't run it in the case where the 'Unsaved' flag is set, so user gets a chance to fully undo the closure + tab->LastFrameVisible = -1; + tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = 0; + } + else if ((tab_bar->VisibleTabId != tab->ID) && (tab->Flags & ImGuiTabItemFlags_UnsavedDocument)) + { + // Actually select before expecting closure + tab_bar->NextSelectedTabId = tab->ID; + } +} + +static float ImGui::TabBarScrollClamp(ImGuiTabBar* tab_bar, float scrolling) +{ + scrolling = ImMin(scrolling, tab_bar->OffsetMax - tab_bar->BarRect.GetWidth()); + return ImMax(scrolling, 0.0f); +} + +static void ImGui::TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab) +{ + ImGuiContext& g = *GImGui; + float margin = g.FontSize * 1.0f; // When to scroll to make Tab N+1 visible always make a bit of N visible to suggest more scrolling area (since we don't have a scrollbar) + int order = tab_bar->GetTabOrder(tab); + float tab_x1 = tab->Offset + (order > 0 ? -margin : 0.0f); + float tab_x2 = tab->Offset + tab->Width + (order + 1 < tab_bar->Tabs.Size ? margin : 1.0f); + tab_bar->ScrollingTargetDistToVisibility = 0.0f; + if (tab_bar->ScrollingTarget > tab_x1) + { + tab_bar->ScrollingTargetDistToVisibility = ImMax(tab_bar->ScrollingAnim - tab_x2, 0.0f); + tab_bar->ScrollingTarget = tab_x1; + } + else if (tab_bar->ScrollingTarget < tab_x2 - tab_bar->BarRect.GetWidth()) + { + tab_bar->ScrollingTargetDistToVisibility = ImMax((tab_x1 - tab_bar->BarRect.GetWidth()) - tab_bar->ScrollingAnim, 0.0f); + tab_bar->ScrollingTarget = tab_x2 - tab_bar->BarRect.GetWidth(); + } +} + +void ImGui::TabBarQueueChangeTabOrder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int dir) +{ + IM_ASSERT(dir == -1 || dir == +1); + IM_ASSERT(tab_bar->ReorderRequestTabId == 0); + tab_bar->ReorderRequestTabId = tab->ID; + tab_bar->ReorderRequestDir = dir; +} + +static ImGuiTabItem* ImGui::TabBarScrollingButtons(ImGuiTabBar* tab_bar) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + const ImVec2 arrow_button_size(g.FontSize - 2.0f, g.FontSize + g.Style.FramePadding.y * 2.0f); + const float scrolling_buttons_width = arrow_button_size.x * 2.0f; + + const ImVec2 backup_cursor_pos = window->DC.CursorPos; + //window->DrawList->AddRect(ImVec2(tab_bar->BarRect.Max.x - scrolling_buttons_width, tab_bar->BarRect.Min.y), ImVec2(tab_bar->BarRect.Max.x, tab_bar->BarRect.Max.y), IM_COL32(255,0,0,255)); + + const ImRect avail_bar_rect = tab_bar->BarRect; + bool want_clip_rect = !avail_bar_rect.Contains(ImRect(window->DC.CursorPos, window->DC.CursorPos + ImVec2(scrolling_buttons_width, 0.0f))); + if (want_clip_rect) + PushClipRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max + ImVec2(g.Style.ItemInnerSpacing.x, 0.0f), true); + + ImGuiTabItem* tab_to_select = NULL; + + int select_dir = 0; + ImVec4 arrow_col = g.Style.Colors[ImGuiCol_Text]; + arrow_col.w *= 0.5f; + + PushStyleColor(ImGuiCol_Text, arrow_col); + PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); + const float backup_repeat_delay = g.IO.KeyRepeatDelay; + const float backup_repeat_rate = g.IO.KeyRepeatRate; + g.IO.KeyRepeatDelay = 0.250f; + g.IO.KeyRepeatRate = 0.200f; + window->DC.CursorPos = ImVec2(tab_bar->BarRect.Max.x - scrolling_buttons_width, tab_bar->BarRect.Min.y); + if (ArrowButtonEx("##<", ImGuiDir_Left, arrow_button_size, ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_Repeat)) + select_dir = -1; + window->DC.CursorPos = ImVec2(tab_bar->BarRect.Max.x - scrolling_buttons_width + arrow_button_size.x, tab_bar->BarRect.Min.y); + if (ArrowButtonEx("##>", ImGuiDir_Right, arrow_button_size, ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_Repeat)) + select_dir = +1; + PopStyleColor(2); + g.IO.KeyRepeatRate = backup_repeat_rate; + g.IO.KeyRepeatDelay = backup_repeat_delay; + + if (want_clip_rect) + PopClipRect(); + + if (select_dir != 0) + if (ImGuiTabItem* tab_item = TabBarFindTabByID(tab_bar, tab_bar->SelectedTabId)) + { + int selected_order = tab_bar->GetTabOrder(tab_item); + int target_order = selected_order + select_dir; + tab_to_select = &tab_bar->Tabs[(target_order >= 0 && target_order < tab_bar->Tabs.Size) ? target_order : selected_order]; // If we are at the end of the list, still scroll to make our tab visible + } + window->DC.CursorPos = backup_cursor_pos; + tab_bar->BarRect.Max.x -= scrolling_buttons_width + 1.0f; + + return tab_to_select; +} + +static ImGuiTabItem* ImGui::TabBarTabListPopupButton(ImGuiTabBar* tab_bar) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + // We use g.Style.FramePadding.y to match the square ArrowButton size + const float tab_list_popup_button_width = g.FontSize + g.Style.FramePadding.y; + const ImVec2 backup_cursor_pos = window->DC.CursorPos; + window->DC.CursorPos = ImVec2(tab_bar->BarRect.Min.x - g.Style.FramePadding.y, tab_bar->BarRect.Min.y); + tab_bar->BarRect.Min.x += tab_list_popup_button_width; + + ImVec4 arrow_col = g.Style.Colors[ImGuiCol_Text]; + arrow_col.w *= 0.5f; + PushStyleColor(ImGuiCol_Text, arrow_col); + PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); + bool open = BeginCombo("##v", NULL, ImGuiComboFlags_NoPreview); + PopStyleColor(2); + + ImGuiTabItem* tab_to_select = NULL; + if (open) + { + for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) + { + ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; + const char* tab_name = tab_bar->GetTabName(tab); + if (Selectable(tab_name, tab_bar->SelectedTabId == tab->ID)) + tab_to_select = tab; + } + EndCombo(); + } + + window->DC.CursorPos = backup_cursor_pos; + return tab_to_select; +} + +//------------------------------------------------------------------------- +// [SECTION] Widgets: BeginTabItem, EndTabItem, etc. +//------------------------------------------------------------------------- +// [BETA API] API may evolve! This code has been extracted out of the Docking branch, +// and some of the construct which are not used in Master may be left here to facilitate merging. +//------------------------------------------------------------------------- +// - BeginTabItem() +// - EndTabItem() +// - TabItemEx() [Internal] +// - SetTabItemClosed() +// - TabItemCalcSize() [Internal] +// - TabItemBackground() [Internal] +// - TabItemLabelAndCloseButton() [Internal] +//------------------------------------------------------------------------- + +bool ImGui::BeginTabItem(const char* label, bool* p_open, ImGuiTabItemFlags flags) +{ + ImGuiContext& g = *GImGui; + if (g.CurrentWindow->SkipItems) + return false; + + ImGuiTabBar* tab_bar = g.CurrentTabBar; + if (tab_bar == NULL) + { + IM_ASSERT(tab_bar && "Needs to be called between BeginTabBar() and EndTabBar()!"); + return false; // FIXME-ERRORHANDLING + } + bool ret = TabItemEx(tab_bar, label, p_open, flags); + if (ret && !(flags & ImGuiTabItemFlags_NoPushId)) + { + ImGuiTabItem* tab = &tab_bar->Tabs[tab_bar->LastTabItemIdx]; + g.CurrentWindow->IDStack.push_back(tab->ID); // We already hashed 'label' so push into the ID stack directly instead of doing another hash through PushID(label) + } + return ret; +} + +void ImGui::EndTabItem() +{ + ImGuiContext& g = *GImGui; + if (g.CurrentWindow->SkipItems) + return; + + ImGuiTabBar* tab_bar = g.CurrentTabBar; + if (tab_bar == NULL) + { + IM_ASSERT(tab_bar != NULL && "Needs to be called between BeginTabBar() and EndTabBar()!"); + return; + } + IM_ASSERT(tab_bar->LastTabItemIdx >= 0); + ImGuiTabItem* tab = &tab_bar->Tabs[tab_bar->LastTabItemIdx]; + if (!(tab->Flags & ImGuiTabItemFlags_NoPushId)) + g.CurrentWindow->IDStack.pop_back(); +} + +bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags) +{ + // Layout whole tab bar if not already done + if (tab_bar->WantLayout) + TabBarLayout(tab_bar); + + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (window->SkipItems) + return false; + + const ImGuiStyle& style = g.Style; + const ImGuiID id = TabBarCalcTabID(tab_bar, label); + + // If the user called us with *p_open == false, we early out and don't render. We make a dummy call to ItemAdd() so that attempts to use a contextual popup menu with an implicit ID won't use an older ID. + if (p_open && !*p_open) + { + PushItemFlag(ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus, true); + ItemAdd(ImRect(), id); + PopItemFlag(); + return false; + } + + // Calculate tab contents size + ImVec2 size = TabItemCalcSize(label, p_open != NULL); + + // Acquire tab data + ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, id); + bool tab_is_new = false; + if (tab == NULL) + { + tab_bar->Tabs.push_back(ImGuiTabItem()); + tab = &tab_bar->Tabs.back(); + tab->ID = id; + tab->Width = size.x; + tab_is_new = true; + } + tab_bar->LastTabItemIdx = (short)tab_bar->Tabs.index_from_ptr(tab); + tab->WidthContents = size.x; + + if (p_open == NULL) + flags |= ImGuiTabItemFlags_NoCloseButton; + + const bool tab_bar_appearing = (tab_bar->PrevFrameVisible + 1 < g.FrameCount); + const bool tab_bar_focused = (tab_bar->Flags & ImGuiTabBarFlags_IsFocused) != 0; + const bool tab_appearing = (tab->LastFrameVisible + 1 < g.FrameCount); + tab->LastFrameVisible = g.FrameCount; + tab->Flags = flags; + + // Append name with zero-terminator + tab->NameOffset = tab_bar->TabsNames.size(); + tab_bar->TabsNames.append(label, label + strlen(label) + 1); + + // If we are not reorderable, always reset offset based on submission order. + // (We already handled layout and sizing using the previous known order, but sizing is not affected by order!) + if (!tab_appearing && !(tab_bar->Flags & ImGuiTabBarFlags_Reorderable)) + { + tab->Offset = tab_bar->OffsetNextTab; + tab_bar->OffsetNextTab += tab->Width + g.Style.ItemInnerSpacing.x; + } + + // Update selected tab + if (tab_appearing && (tab_bar->Flags & ImGuiTabBarFlags_AutoSelectNewTabs) && tab_bar->NextSelectedTabId == 0) + if (!tab_bar_appearing || tab_bar->SelectedTabId == 0) + tab_bar->NextSelectedTabId = id; // New tabs gets activated + if ((flags & ImGuiTabItemFlags_SetSelected) && (tab_bar->SelectedTabId != id)) // SetSelected can only be passed on explicit tab bar + tab_bar->NextSelectedTabId = id; + + // Lock visibility + bool tab_contents_visible = (tab_bar->VisibleTabId == id); + if (tab_contents_visible) + tab_bar->VisibleTabWasSubmitted = true; + + // On the very first frame of a tab bar we let first tab contents be visible to minimize appearing glitches + if (!tab_contents_visible && tab_bar->SelectedTabId == 0 && tab_bar_appearing) + if (tab_bar->Tabs.Size == 1 && !(tab_bar->Flags & ImGuiTabBarFlags_AutoSelectNewTabs)) + tab_contents_visible = true; + + if (tab_appearing && !(tab_bar_appearing && !tab_is_new)) + { + PushItemFlag(ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus, true); + ItemAdd(ImRect(), id); + PopItemFlag(); + return tab_contents_visible; + } + + if (tab_bar->SelectedTabId == id) + tab->LastFrameSelected = g.FrameCount; + + // Backup current layout position + const ImVec2 backup_main_cursor_pos = window->DC.CursorPos; + + // Layout + size.x = tab->Width; + window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2((float)(int)tab->Offset - tab_bar->ScrollingAnim, 0.0f); + ImVec2 pos = window->DC.CursorPos; + ImRect bb(pos, pos + size); + + // We don't have CPU clipping primitives to clip the CloseButton (until it becomes a texture), so need to add an extra draw call (temporary in the case of vertical animation) + bool want_clip_rect = (bb.Min.x < tab_bar->BarRect.Min.x) || (bb.Max.x >= tab_bar->BarRect.Max.x); + if (want_clip_rect) + PushClipRect(ImVec2(ImMax(bb.Min.x, tab_bar->BarRect.Min.x), bb.Min.y - 1), ImVec2(tab_bar->BarRect.Max.x, bb.Max.y), true); + + ItemSize(bb, style.FramePadding.y); + if (!ItemAdd(bb, id)) + { + if (want_clip_rect) + PopClipRect(); + window->DC.CursorPos = backup_main_cursor_pos; + return tab_contents_visible; + } + + // Click to Select a tab + ImGuiButtonFlags button_flags = (ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_AllowItemOverlap); + if (g.DragDropActive) + button_flags |= ImGuiButtonFlags_PressedOnDragDropHold; + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); + if (pressed) + tab_bar->NextSelectedTabId = id; + hovered |= (g.HoveredId == id); + + // Allow the close button to overlap unless we are dragging (in which case we don't want any overlapping tabs to be hovered) + if (!held) + SetItemAllowOverlap(); + + // Drag and drop: re-order tabs + if (held && !tab_appearing && IsMouseDragging(0)) + { + if (!g.DragDropActive && (tab_bar->Flags & ImGuiTabBarFlags_Reorderable)) + { + // While moving a tab it will jump on the other side of the mouse, so we also test for MouseDelta.x + if (g.IO.MouseDelta.x < 0.0f && g.IO.MousePos.x < bb.Min.x) + { + if (tab_bar->Flags & ImGuiTabBarFlags_Reorderable) + TabBarQueueChangeTabOrder(tab_bar, tab, -1); + } + else if (g.IO.MouseDelta.x > 0.0f && g.IO.MousePos.x > bb.Max.x) + { + if (tab_bar->Flags & ImGuiTabBarFlags_Reorderable) + TabBarQueueChangeTabOrder(tab_bar, tab, +1); + } + } + } + +#if 0 + if (hovered && g.HoveredIdNotActiveTimer > 0.50f && bb.GetWidth() < tab->WidthContents) + { + // Enlarge tab display when hovering + bb.Max.x = bb.Min.x + (float)(int)ImLerp(bb.GetWidth(), tab->WidthContents, ImSaturate((g.HoveredIdNotActiveTimer - 0.40f) * 6.0f)); + display_draw_list = GetForegroundDrawList(window); + TabItemBackground(display_draw_list, bb, flags, GetColorU32(ImGuiCol_TitleBgActive)); + } +#endif + + // Render tab shape + ImDrawList* display_draw_list = window->DrawList; + const ImU32 tab_col = GetColorU32((held || hovered) ? ImGuiCol_TabHovered : tab_contents_visible ? (tab_bar_focused ? ImGuiCol_TabActive : ImGuiCol_TabUnfocusedActive) : (tab_bar_focused ? ImGuiCol_Tab : ImGuiCol_TabUnfocused)); + TabItemBackground(display_draw_list, bb, flags, tab_col); + RenderNavHighlight(bb, id); + + // Select with right mouse button. This is so the common idiom for context menu automatically highlight the current widget. + const bool hovered_unblocked = IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup); + if (hovered_unblocked && (IsMouseClicked(1) || IsMouseReleased(1))) + tab_bar->NextSelectedTabId = id; + + if (tab_bar->Flags & ImGuiTabBarFlags_NoCloseWithMiddleMouseButton) + flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton; + + // Render tab label, process close button + const ImGuiID close_button_id = p_open ? window->GetID((void*)((intptr_t)id + 1)) : 0; + bool just_closed = TabItemLabelAndCloseButton(display_draw_list, bb, flags, tab_bar->FramePadding, label, id, close_button_id); + if (just_closed && p_open != NULL) + { + *p_open = false; + TabBarCloseTab(tab_bar, tab); + } + + // Restore main window position so user can draw there + if (want_clip_rect) + PopClipRect(); + window->DC.CursorPos = backup_main_cursor_pos; + + // Tooltip (FIXME: Won't work over the close button because ItemOverlap systems messes up with HoveredIdTimer) + // We test IsItemHovered() to discard e.g. when another item is active or drag and drop over the tab bar (which g.HoveredId ignores) + if (g.HoveredId == id && !held && g.HoveredIdNotActiveTimer > 0.50f && IsItemHovered()) + if (!(tab_bar->Flags & ImGuiTabBarFlags_NoTooltip)) + SetTooltip("%.*s", (int)(FindRenderedTextEnd(label) - label), label); + + return tab_contents_visible; +} + +// [Public] This is call is 100% optional but it allows to remove some one-frame glitches when a tab has been unexpectedly removed. +// To use it to need to call the function SetTabItemClosed() after BeginTabBar() and before any call to BeginTabItem() +void ImGui::SetTabItemClosed(const char* label) +{ + ImGuiContext& g = *GImGui; + bool is_within_manual_tab_bar = g.CurrentTabBar && !(g.CurrentTabBar->Flags & ImGuiTabBarFlags_DockNode); + if (is_within_manual_tab_bar) + { + ImGuiTabBar* tab_bar = g.CurrentTabBar; + IM_ASSERT(tab_bar->WantLayout); // Needs to be called AFTER BeginTabBar() and BEFORE the first call to BeginTabItem() + ImGuiID tab_id = TabBarCalcTabID(tab_bar, label); + TabBarRemoveTab(tab_bar, tab_id); + } +} + +ImVec2 ImGui::TabItemCalcSize(const char* label, bool has_close_button) +{ + ImGuiContext& g = *GImGui; + ImVec2 label_size = CalcTextSize(label, NULL, true); + ImVec2 size = ImVec2(label_size.x + g.Style.FramePadding.x, label_size.y + g.Style.FramePadding.y * 2.0f); + if (has_close_button) + size.x += g.Style.FramePadding.x + (g.Style.ItemInnerSpacing.x + g.FontSize); // We use Y intentionally to fit the close button circle. + else + size.x += g.Style.FramePadding.x + 1.0f; + return ImVec2(ImMin(size.x, TabBarCalcMaxTabWidth()), size.y); +} + +void ImGui::TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImU32 col) +{ + // While rendering tabs, we trim 1 pixel off the top of our bounding box so they can fit within a regular frame height while looking "detached" from it. + ImGuiContext& g = *GImGui; + const float width = bb.GetWidth(); + IM_UNUSED(flags); + IM_ASSERT(width > 0.0f); + const float rounding = ImMax(0.0f, ImMin(g.Style.TabRounding, width * 0.5f - 1.0f)); + const float y1 = bb.Min.y + 1.0f; + const float y2 = bb.Max.y - 1.0f; + draw_list->PathLineTo(ImVec2(bb.Min.x, y2)); + draw_list->PathArcToFast(ImVec2(bb.Min.x + rounding, y1 + rounding), rounding, 6, 9); + draw_list->PathArcToFast(ImVec2(bb.Max.x - rounding, y1 + rounding), rounding, 9, 12); + draw_list->PathLineTo(ImVec2(bb.Max.x, y2)); + draw_list->PathFillConvex(col); + if (g.Style.TabBorderSize > 0.0f) + { + draw_list->PathLineTo(ImVec2(bb.Min.x + 0.5f, y2)); + draw_list->PathArcToFast(ImVec2(bb.Min.x + rounding + 0.5f, y1 + rounding + 0.5f), rounding, 6, 9); + draw_list->PathArcToFast(ImVec2(bb.Max.x - rounding - 0.5f, y1 + rounding + 0.5f), rounding, 9, 12); + draw_list->PathLineTo(ImVec2(bb.Max.x - 0.5f, y2)); + draw_list->PathStroke(GetColorU32(ImGuiCol_Border), false, g.Style.TabBorderSize); + } +} + +// Render text label (with custom clipping) + Unsaved Document marker + Close Button logic +// We tend to lock style.FramePadding for a given tab-bar, hence the 'frame_padding' parameter. +bool ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImVec2 frame_padding, const char* label, ImGuiID tab_id, ImGuiID close_button_id) +{ + ImGuiContext& g = *GImGui; + ImVec2 label_size = CalcTextSize(label, NULL, true); + if (bb.GetWidth() <= 1.0f) + return false; + + // Render text label (with clipping + alpha gradient) + unsaved marker + const char* TAB_UNSAVED_MARKER = "*"; + ImRect text_pixel_clip_bb(bb.Min.x + frame_padding.x, bb.Min.y + frame_padding.y, bb.Max.x - frame_padding.x, bb.Max.y); + if (flags & ImGuiTabItemFlags_UnsavedDocument) + { + text_pixel_clip_bb.Max.x -= CalcTextSize(TAB_UNSAVED_MARKER, NULL, false).x; + ImVec2 unsaved_marker_pos(ImMin(bb.Min.x + frame_padding.x + label_size.x + 2, text_pixel_clip_bb.Max.x), bb.Min.y + frame_padding.y + (float)(int)(-g.FontSize * 0.25f)); + RenderTextClippedEx(draw_list, unsaved_marker_pos, bb.Max - frame_padding, TAB_UNSAVED_MARKER, NULL, NULL); + } + ImRect text_ellipsis_clip_bb = text_pixel_clip_bb; + + // Close Button + // We are relying on a subtle and confusing distinction between 'hovered' and 'g.HoveredId' which happens because we are using ImGuiButtonFlags_AllowOverlapMode + SetItemAllowOverlap() + // 'hovered' will be true when hovering the Tab but NOT when hovering the close button + // 'g.HoveredId==id' will be true when hovering the Tab including when hovering the close button + // 'g.ActiveId==close_button_id' will be true when we are holding on the close button, in which case both hovered booleans are false + bool close_button_pressed = false; + bool close_button_visible = false; + if (close_button_id != 0) + if (g.HoveredId == tab_id || g.HoveredId == close_button_id || g.ActiveId == close_button_id) + close_button_visible = true; + if (close_button_visible) + { + ImGuiItemHoveredDataBackup last_item_backup; + const float close_button_sz = g.FontSize * 0.5f; + if (CloseButton(close_button_id, ImVec2(bb.Max.x - frame_padding.x - close_button_sz, bb.Min.y + frame_padding.y + close_button_sz), close_button_sz)) + close_button_pressed = true; + last_item_backup.Restore(); + + // Close with middle mouse button + if (!(flags & ImGuiTabItemFlags_NoCloseWithMiddleMouseButton) && IsMouseClicked(2)) + close_button_pressed = true; + + text_pixel_clip_bb.Max.x -= close_button_sz * 2.0f; + } + + // Label with ellipsis + // FIXME: This should be extracted into a helper but the use of text_pixel_clip_bb and !close_button_visible makes it tricky to abstract at the moment + const char* label_display_end = FindRenderedTextEnd(label); + if (label_size.x > text_ellipsis_clip_bb.GetWidth()) + { + const int ellipsis_dot_count = 3; + const float ellipsis_width = (1.0f + 1.0f) * ellipsis_dot_count - 1.0f; + const char* label_end = NULL; + float label_size_clipped_x = g.Font->CalcTextSizeA(g.FontSize, text_ellipsis_clip_bb.GetWidth() - ellipsis_width + 1.0f, 0.0f, label, label_display_end, &label_end).x; + if (label_end == label && label_end < label_display_end) // Always display at least 1 character if there's no room for character + ellipsis + { + label_end = label + ImTextCountUtf8BytesFromChar(label, label_display_end); + label_size_clipped_x = g.Font->CalcTextSizeA(g.FontSize, FLT_MAX, 0.0f, label, label_end).x; + } + while (label_end > label && ImCharIsBlankA(label_end[-1])) // Trim trailing space + { + label_end--; + label_size_clipped_x -= g.Font->CalcTextSizeA(g.FontSize, FLT_MAX, 0.0f, label_end, label_end + 1).x; // Ascii blanks are always 1 byte + } + RenderTextClippedEx(draw_list, text_pixel_clip_bb.Min, text_pixel_clip_bb.Max, label, label_end, &label_size, ImVec2(0.0f, 0.0f)); + + const float ellipsis_x = text_pixel_clip_bb.Min.x + label_size_clipped_x + 1.0f; + if (!close_button_visible && ellipsis_x + ellipsis_width <= bb.Max.x) + RenderPixelEllipsis(draw_list, ImVec2(ellipsis_x, text_pixel_clip_bb.Min.y), ellipsis_dot_count, GetColorU32(ImGuiCol_Text)); + } + else + { + RenderTextClippedEx(draw_list, text_pixel_clip_bb.Min, text_pixel_clip_bb.Max, label, label_display_end, &label_size, ImVec2(0.0f, 0.0f)); + } + + return close_button_pressed; +} diff --git a/src/lib/geogram_gfx/third_party/ImGui/imstb_rectpack.h b/src/lib/geogram_gfx/third_party/ImGui/imstb_rectpack.h index 2b07dcc8..23f922a5 100644 --- a/src/lib/geogram_gfx/third_party/ImGui/imstb_rectpack.h +++ b/src/lib/geogram_gfx/third_party/ImGui/imstb_rectpack.h @@ -1,4 +1,10 @@ -// stb_rect_pack.h - v0.11 - public domain - rectangle packing +// [DEAR IMGUI] +// This is a slightly modified version of stb_rect_pack.h 0.99. +// Those changes would need to be pushed into nothings/stb: +// - Added STBRP__CDECL +// Grep for [DEAR IMGUI] to find the changes. + +// stb_rect_pack.h - v0.99 - public domain - rectangle packing // Sean Barrett 2014 // // Useful for e.g. packing rectangular textures into an atlas. @@ -34,6 +40,7 @@ // // Version history: // +// 0.99 (2019-02-07) warning fixes // 0.11 (2017-03-03) return packing success/fail result // 0.10 (2016-10-25) remove cast-away-const to avoid warnings // 0.09 (2016-08-27) fix compiler warnings @@ -204,6 +211,7 @@ struct stbrp_context #define STBRP_ASSERT assert #endif +// [DEAR IMGUI] Added STBRP__CDECL #ifdef _MSC_VER #define STBRP__NOTUSED(v) (void)(v) #define STBRP__CDECL __cdecl @@ -512,6 +520,7 @@ static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, i return res; } +// [DEAR IMGUI] Added STBRP__CDECL static int STBRP__CDECL rect_height_compare(const void *a, const void *b) { const stbrp_rect *p = (const stbrp_rect *) a; @@ -523,6 +532,7 @@ static int STBRP__CDECL rect_height_compare(const void *a, const void *b) return (p->w > q->w) ? -1 : (p->w < q->w); } +// [DEAR IMGUI] Added STBRP__CDECL static int STBRP__CDECL rect_original_order(const void *a, const void *b) { const stbrp_rect *p = (const stbrp_rect *) a; @@ -543,9 +553,6 @@ STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int nu // we use the 'was_packed' field internally to allow sorting/unsorting for (i=0; i < num_rects; ++i) { rects[i].was_packed = i; - #ifndef STBRP_LARGE_RECTS - STBRP_ASSERT(rects[i].w <= 0xffff && rects[i].h <= 0xffff); - #endif } // sort according to heuristic diff --git a/src/lib/geogram_gfx/third_party/ImGui/imstb_textedit.h b/src/lib/geogram_gfx/third_party/ImGui/imstb_textedit.h index 9e12469b..d7fcbd62 100644 --- a/src/lib/geogram_gfx/third_party/ImGui/imstb_textedit.h +++ b/src/lib/geogram_gfx/third_party/ImGui/imstb_textedit.h @@ -1,9 +1,10 @@ -// [ImGui] this is a slightly modified version of stb_textedit.h 1.12. Those changes would need to be pushed into nothings/stb -// [ImGui] - 2018-06: fixed undo/redo after pasting large amount of text (over 32 kb). Redo will still fail when undo buffers are exhausted, but text won't be corrupted (see nothings/stb issue #620) -// [ImGui] - 2018-06: fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321) -// [ImGui] - fixed some minor warnings +// [DEAR IMGUI] +// This is a slightly modified version of stb_textedit.h 1.13. +// Those changes would need to be pushed into nothings/stb: +// - Fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321) +// Grep for [DEAR IMGUI] to find the changes. -// stb_textedit.h - v1.12 - public domain - Sean Barrett +// stb_textedit.h - v1.13 - public domain - Sean Barrett // Development of this library was sponsored by RAD Game Tools // // This C header file implements the guts of a multi-line text-editing @@ -34,6 +35,7 @@ // // VERSION HISTORY // +// 1.13 (2019-02-07) fix bug in undo size management // 1.12 (2018-01-29) user can change STB_TEXTEDIT_KEYTYPE, fix redo to avoid crash // 1.11 (2017-03-03) fix HOME on last line, dragging off single-line textfield // 1.10 (2016-10-25) supress warnings about casting away const with -Wcast-qual @@ -563,7 +565,6 @@ static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *s // now scan to find xpos find->x = r.x0; - i = 0; for (i=0; first+i < n; ++i) find->x += STB_TEXTEDIT_GETWIDTH(str, first, i); } @@ -693,7 +694,7 @@ static void stb_textedit_prep_selection_at_cursor(STB_TexteditState *state) static int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) { if (STB_TEXT_HAS_SELECTION(state)) { - stb_textedit_delete_selection(str,state); // implicity clamps + stb_textedit_delete_selection(str,state); // implicitly clamps state->has_preferred_x = 0; return 1; } @@ -745,7 +746,7 @@ static void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, state->has_preferred_x = 0; } } else { - stb_textedit_delete_selection(str,state); // implicity clamps + stb_textedit_delete_selection(str,state); // implicitly clamps if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) { stb_text_makeundo_insert(state, state->cursor, 1); ++state->cursor; @@ -1133,7 +1134,14 @@ static void stb_textedit_discard_redo(StbUndoState *state) state->undo_rec[i].char_storage += n; } // now move all the redo records towards the end of the buffer; the first one is at 'redo_point' - STB_TEXTEDIT_memmove(state->undo_rec + state->redo_point+1, state->undo_rec + state->redo_point, (size_t) ((STB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point)*sizeof(state->undo_rec[0]))); + // {DEAR IMGUI] + size_t move_size = (size_t)((STB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point - 1) * sizeof(state->undo_rec[0])); + const char* buf_begin = (char*)state->undo_rec; (void)buf_begin; + const char* buf_end = (char*)state->undo_rec + sizeof(state->undo_rec); (void)buf_end; + IM_ASSERT(((char*)(state->undo_rec + state->redo_point)) >= buf_begin); + IM_ASSERT(((char*)(state->undo_rec + state->redo_point + 1) + move_size) <= buf_end); + STB_TEXTEDIT_memmove(state->undo_rec + state->redo_point+1, state->undo_rec + state->redo_point, move_size); + // now move redo_point to point to the new one ++state->redo_point; } diff --git a/src/lib/geogram_gfx/third_party/ImGui/imstb_truetype.h b/src/lib/geogram_gfx/third_party/ImGui/imstb_truetype.h index f65deb50..c1cdb180 100644 --- a/src/lib/geogram_gfx/third_party/ImGui/imstb_truetype.h +++ b/src/lib/geogram_gfx/third_party/ImGui/imstb_truetype.h @@ -1,4 +1,9 @@ -// stb_truetype.h - v1.19 - public domain +// [DEAR IMGUI] +// This is a slightly modified version of stb_truetype.h 1.20. +// Mostly fixing for compiler and static analyzer warnings. +// Grep for [DEAR IMGUI] to find the changes. + +// stb_truetype.h - v1.20 - public domain // authored from 2009-2016 by Sean Barrett / RAD Game Tools // // This library processes TrueType files: @@ -49,6 +54,7 @@ // // VERSION HISTORY // +// 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics() // 1.19 (2018-02-11) GPOS kerning, STBTT_fmod // 1.18 (2018-01-29) add missing function // 1.17 (2017-07-23) make more arguments const; doc fix @@ -75,7 +81,7 @@ // // USAGE // -// Include this file in whatever places neeed to refer to it. In ONE C/C++ +// Include this file in whatever places need to refer to it. In ONE C/C++ // file, write: // #define STB_TRUETYPE_IMPLEMENTATION // before the #include of this file. This expands out the actual @@ -247,8 +253,8 @@ // Documentation & header file 520 LOC \___ 660 LOC documentation // Sample code 140 LOC / // Truetype parsing 620 LOC ---- 620 LOC TrueType -// Software rasterization 240 LOC \ . -// Curve tesselation 120 LOC \__ 550 LOC Bitmap creation +// Software rasterization 240 LOC \. +// Curve tessellation 120 LOC \__ 550 LOC Bitmap creation // Bitmap management 100 LOC / // Baked bitmap interface 70 LOC / // Font name matching & access 150 LOC ---- 150 @@ -556,6 +562,8 @@ STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int p // // It's inefficient; you might want to c&p it and optimize it. +STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap); +// Query the font vertical metrics without having to create a font first. ////////////////////////////////////////////////////////////////////////////// @@ -641,6 +649,12 @@ STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h // To use with PackFontRangesGather etc., you must set it before calls // call to PackFontRangesGatherRects. +STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip); +// If skip != 0, this tells stb_truetype to skip any codepoints for which +// there is no corresponding glyph. If skip=0, which is the default, then +// codepoints without a glyph recived the font's "missing character" glyph, +// typically an empty box by convention. + STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, // same data as above int char_index, // character to display float *xpos, float *ypos, // pointers to current position in screen pixel space @@ -669,6 +683,7 @@ struct stbtt_pack_context { int height; int stride_in_bytes; int padding; + int skip_missing; unsigned int h_oversample, v_oversample; unsigned char *pixels; void *nodes; @@ -694,7 +709,7 @@ STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index); // file will only define one font and it always be at offset 0, so it will // return '0' for index 0, and -1 for all other indices. -// The following structure is defined publically so you can declare one on +// The following structure is defined publicly so you can declare one on // the stack or as a global or etc, but you should treat it as opaque. struct stbtt_fontinfo { @@ -733,6 +748,7 @@ STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codep // and you want a speed-up, call this function with the character you're // going to process, then use glyph-based functions instead of the // codepoint-based functions. +// Returns 0 if the character codepoint is not defined in the font. ////////////////////////////////////////////////////////////////////////////// @@ -820,7 +836,7 @@ STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, s // returns # of vertices and fills *vertices with the pointer to them // these are expressed in "unscaled" coordinates // -// The shape is a series of countours. Each one starts with +// The shape is a series of contours. Each one starts with // a STBTT_moveto, then consists of a series of mixed // STBTT_lineto and STBTT_curveto segments. A lineto // draws a line from previous endpoint to its x,y; a curveto @@ -916,7 +932,7 @@ STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float sc STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); // These functions compute a discretized SDF field for a single character, suitable for storing // in a single-channel texture, sampling with bilinear filtering, and testing against -// larger than some threshhold to produce scalable fonts. +// larger than some threshold to produce scalable fonts. // info -- the font // scale -- controls the size of the resulting SDF bitmap, same as it would be creating a regular bitmap // glyph/codepoint -- the character to generate the SDF for @@ -1825,7 +1841,7 @@ static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, s if (comp_verts) STBTT_free(comp_verts, info->userdata); return 0; } - if (num_vertices > 0) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex)); + if (num_vertices > 0) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex)); //-V595 STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex)); if (vertices) STBTT_free(vertices, info->userdata); vertices = tmp; @@ -2196,7 +2212,7 @@ static int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, st } break; default: - if (b0 != 255 && b0 != 28 && (b0 < 32 || b0 > 254)) + if (b0 != 255 && b0 != 28 && (b0 < 32 || b0 > 254)) //-V560 return STBTT__CSERR("reserved operator"); // push immediate @@ -2368,7 +2384,8 @@ static stbtt_int32 stbtt__GetGlyphClass(stbtt_uint8 *classDefTable, int glyph) if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount) return (stbtt_int32)ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID)); - classDefTable = classDef1ValueArray + 2 * glyphCount; + // [DEAR IMGUI] Commented to fix static analyzer warning + //classDefTable = classDef1ValueArray + 2 * glyphCount; } break; case 2: { @@ -2392,7 +2409,8 @@ static stbtt_int32 stbtt__GetGlyphClass(stbtt_uint8 *classDefTable, int glyph) return (stbtt_int32)ttUSHORT(classRangeRecord + 4); } - classDefTable = classRangeRecords + 6 * classRangeCount; + // [DEAR IMGUI] Commented to fix static analyzer warning + //classDefTable = classRangeRecords + 6 * classRangeCount; } break; default: { @@ -3024,6 +3042,8 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, dx = -dx; dy = -dy; t = x0, x0 = xb, xb = t; + // [DEAR IMGUI] Fix static analyzer warning + (void)dx; // [ImGui: fix static analyzer warning] } x1 = (int) x_top; @@ -3161,7 +3181,13 @@ static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, if (e->y0 != e->y1) { stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata); if (z != NULL) { - STBTT_assert(z->ey >= scan_y_top); + if (j == 0 && off_y != 0) { + if (z->ey < scan_y_top) { + // this can happen due to subpixel positioning and some kind of fp rounding error i think + z->ey = scan_y_top; + } + } + STBTT_assert(z->ey >= scan_y_top); // if we get really unlucky a tiny bit of an edge can be out of bounds // insert at front z->next = active; active = z; @@ -3230,7 +3256,7 @@ static void stbtt__sort_edges_ins_sort(stbtt__edge *p, int n) static void stbtt__sort_edges_quicksort(stbtt__edge *p, int n) { - /* threshhold for transitioning to insertion sort */ + /* threshold for transitioning to insertion sort */ while (n > 12) { stbtt__edge t; int c01,c12,c,m,i,j; @@ -3365,7 +3391,7 @@ static void stbtt__add_point(stbtt__point *points, int n, float x, float y) points[n].y = y; } -// tesselate until threshhold p is happy... @TODO warped to compensate for non-linear stretching +// tessellate until threshold p is happy... @TODO warped to compensate for non-linear stretching static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n) { // midpoint @@ -3790,6 +3816,7 @@ STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, in spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw; spc->h_oversample = 1; spc->v_oversample = 1; + spc->skip_missing = 0; stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes); @@ -3815,6 +3842,11 @@ STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h spc->v_oversample = v_oversample; } +STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip) +{ + spc->skip_missing = skip; +} + #define STBTT__OVER_MASK (STBTT_MAX_OVERSAMPLE-1) static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) @@ -3968,13 +4000,17 @@ STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stb int x0,y0,x1,y1; int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; int glyph = stbtt_FindGlyphIndex(info, codepoint); - stbtt_GetGlyphBitmapBoxSubpixel(info,glyph, - scale * spc->h_oversample, - scale * spc->v_oversample, - 0,0, - &x0,&y0,&x1,&y1); - rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1); - rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1); + if (glyph == 0 && spc->skip_missing) { + rects[k].w = rects[k].h = 0; + } else { + stbtt_GetGlyphBitmapBoxSubpixel(info,glyph, + scale * spc->h_oversample, + scale * spc->v_oversample, + 0,0, + &x0,&y0,&x1,&y1); + rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1); + rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1); + } ++k; } } @@ -4027,7 +4063,7 @@ STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const sub_y = stbtt__oversample_shift(spc->v_oversample); for (j=0; j < ranges[i].num_chars; ++j) { stbrp_rect *r = &rects[k]; - if (r->was_packed) { + if (r->was_packed && r->w != 0 && r->h != 0) { stbtt_packedchar *bc = &ranges[i].chardata_for_range[j]; int advance, lsb, x0,y0,x1,y1; int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; @@ -4141,6 +4177,19 @@ STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char * return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1); } +STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap) +{ + int i_ascent, i_descent, i_lineGap; + float scale; + stbtt_fontinfo info; + stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata, index)); + scale = size > 0 ? stbtt_ScaleForPixelHeight(&info, size) : stbtt_ScaleForMappingEmToPixels(&info, -size); + stbtt_GetFontVMetrics(&info, &i_ascent, &i_descent, &i_lineGap); + *ascent = (float) i_ascent * scale; + *descent = (float) i_descent * scale; + *lineGap = (float) i_lineGap * scale; +} + STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer) { float ipw = 1.0f / pw, iph = 1.0f / ph; @@ -4253,7 +4302,7 @@ static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex int winding = 0; orig[0] = x; - orig[1] = y; + //orig[1] = y; // [DEAR IMGUI] commmented double assignment // make sure y never passes through a vertex of the shape y_frac = (float) STBTT_fmod(y, 1.0f); diff --git a/src/lib/geogram_gfx/third_party/ImGuiColorTextEdit/TextEditor.cpp b/src/lib/geogram_gfx/third_party/ImGuiColorTextEdit/TextEditor.cpp index ec3e1d8b..b3f9512b 100755 --- a/src/lib/geogram_gfx/third_party/ImGuiColorTextEdit/TextEditor.cpp +++ b/src/lib/geogram_gfx/third_party/ImGuiColorTextEdit/TextEditor.cpp @@ -598,23 +598,23 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) callback_(TEXT_EDITOR_COMPLETION, callback_client_data_); } } - - if (!IsReadOnly()) - { - for (size_t i = 0; i < sizeof(io.InputCharacters) / sizeof(io.InputCharacters[0]); i++) - { - auto c = (unsigned char)io.InputCharacters[i]; - if (c != 0) - { - if (isprint(c) || isspace(c)) - { - if (c == '\r') - c = '\n'; - EnterCharacter((char)c); - } + + // [Bruno Levy] ported to ImGui 1.69 + if(!IsReadOnly()) { + for(int i=0; i0.0f: fixed size. each axis can use a different mode, e.g. ImVec2 0 400. +// IMGUI_API bool BeginChild(const char* str_id, const ImVec2& size = ImVec2 0 0, bool border = false, ImGuiWindowFlags flags = 0); IMGUI_FUNCTION(BeginChild) LABEL_ARG(str_id) OPTIONAL_IM_VEC_2_ARG(size, 0, 0) @@ -120,7 +131,7 @@ END_IMGUI_FUNC // Unsupported arg type ImGuiFocusedFlags flags=0 // IMGUI_API bool IsWindowHovered(ImGuiHoveredFlags flags=0); // is current window hovered (and typically: not blocked by a popup/modal)? see flags for options. NB: If you are trying to check whether your mouse should be dispatched to imgui or to your app, you should use the 'io.WantCaptureMouse' boolean for that! Please read the FAQ! // Unsupported arg type ImGuiHoveredFlags flags=0 -// IMGUI_API ImDrawList* GetWindowDrawList(); // get draw list associated to the window, to append your own drawing primitives +// IMGUI_API ImDrawList* GetWindowDrawList(); // get draw list associated to the current window, to append your own drawing primitives // Unsupported return type ImDrawList* // IMGUI_API ImVec2 GetWindowPos(); // get current window position in screen space (useful if you want to do your own drawing via the DrawList API) IMGUI_FUNCTION(GetWindowPos) @@ -156,7 +167,7 @@ CALL_FUNCTION(GetContentRegionAvail, ImVec2) PUSH_NUMBER(ret.x) PUSH_NUMBER(ret.y) END_IMGUI_FUNC -// IMGUI_API float GetContentRegionAvailWidth(); // +// IMGUI_API float GetContentRegionAvailWidth(); // == GetContentRegionAvail().x IMGUI_FUNCTION(GetContentRegionAvailWidth) CALL_FUNCTION(GetContentRegionAvailWidth, float) PUSH_NUMBER(ret) @@ -196,14 +207,14 @@ END_IMGUI_FUNC IMGUI_FUNCTION(SetNextWindowFocus) CALL_FUNCTION_NO_RET(SetNextWindowFocus) END_IMGUI_FUNC -// IMGUI_API void SetNextWindowBgAlpha(float alpha); // set next window background color alpha. helper to easily modify ImGuiCol_WindowBg/ChildBg/PopupBg. +// IMGUI_API void SetNextWindowBgAlpha(float alpha); // set next window background color alpha. helper to easily modify ImGuiCol_WindowBg/ChildBg/PopupBg. you may also use ImGuiWindowFlags_NoBackground. IMGUI_FUNCTION(SetNextWindowBgAlpha) NUMBER_ARG(alpha) CALL_FUNCTION_NO_RET(SetNextWindowBgAlpha, alpha) END_IMGUI_FUNC // IMGUI_API void SetWindowPos(const ImVec2& pos, ImGuiCond cond = 0); // (not recommended) set current window position - call within Begin()/End(). prefer using SetNextWindowPos(), as this may incur tearing and side-effects. // Unsupported arg type ImGuiCond cond = 0 -// IMGUI_API void SetWindowSize(const ImVec2& size, ImGuiCond cond = 0); // (not recommended) set current window size - call within Begin()/End(). set to ImVec2 0 0 to force an auto-fit. prefer using SetNextWindowSize(), as this may incur tearing and minor side-effects. +// IMGUI_API void SetWindowSize(const ImVec2& size, ImGuiCond cond = 0); // (not recommended) set current window size - call within Begin()/End(). set to ImVec2 0 0 to force an auto-fit. prefer using SetNextWindowSize(), as this may incur tearing and minor side-effects. // Unsupported arg type ImGuiCond cond = 0 // IMGUI_API void SetWindowCollapsed(bool collapsed, ImGuiCond cond = 0); // (not recommended) set current window collapsed state. prefer using SetNextWindowCollapsed(). // Unsupported arg type ImGuiCond cond = 0 @@ -257,16 +268,16 @@ IMGUI_FUNCTION(SetScrollY) NUMBER_ARG(scroll_y) CALL_FUNCTION_NO_RET(SetScrollY, scroll_y) END_IMGUI_FUNC -// IMGUI_API void SetScrollHere(float center_y_ratio = 0.5f); // adjust scrolling amount to make current cursor position visible. center_y_ratio=0.0: top, 0.5: center, 1.0: bottom. When using to make a "default/current item" visible, consider using SetItemDefaultFocus() instead. -IMGUI_FUNCTION(SetScrollHere) +// IMGUI_API void SetScrollHereY(float center_y_ratio = 0.5f); // adjust scrolling amount to make current cursor position visible. center_y_ratio=0.0: top, 0.5: center, 1.0: bottom. When using to make a "default/current item" visible, consider using SetItemDefaultFocus() instead. +IMGUI_FUNCTION(SetScrollHereY) OPTIONAL_NUMBER_ARG(center_y_ratio, 0.5f) -CALL_FUNCTION_NO_RET(SetScrollHere, center_y_ratio) +CALL_FUNCTION_NO_RET(SetScrollHereY, center_y_ratio) END_IMGUI_FUNC -// IMGUI_API void SetScrollFromPosY(float pos_y, float center_y_ratio = 0.5f); // adjust scrolling amount to make given position valid. use GetCursorPos() or GetCursorStartPos()+offset to get valid positions. +// IMGUI_API void SetScrollFromPosY(float local_y, float center_y_ratio = 0.5f); // adjust scrolling amount to make given position visible. Generally GetCursorStartPos() + offset to compute a valid position. IMGUI_FUNCTION(SetScrollFromPosY) -NUMBER_ARG(pos_y) +NUMBER_ARG(local_y) OPTIONAL_NUMBER_ARG(center_y_ratio, 0.5f) -CALL_FUNCTION_NO_RET(SetScrollFromPosY, pos_y, center_y_ratio) +CALL_FUNCTION_NO_RET(SetScrollFromPosY, local_y, center_y_ratio) END_IMGUI_FUNC // IMGUI_API void PushFont(ImFont* font); // use NULL as a shortcut to push default font // Unsupported arg type ImFont* font @@ -311,7 +322,7 @@ OPTIONAL_INT_ARG(count, 1) CALL_FUNCTION_NO_RET(PopStyleVar, count) POP_END_STACK(3) END_IMGUI_FUNC -// IMGUI_API const ImVec4& GetStyleColorVec4(ImGuiCol idx); // retrieve style color as stored in ImGuiStyle structure. use to feed back into PushStyleColor(), otherwhise use GetColorU32() to get style color with style alpha baked in. +// IMGUI_API const ImVec4& GetStyleColorVec4(ImGuiCol idx); // retrieve style color as stored in ImGuiStyle structure. use to feed back into PushStyleColor(), otherwise use GetColorU32() to get style color with style alpha baked in. // Unsupported return type const // IMGUI_API ImFont* GetFont(); // get current font // Unsupported return type ImFont* @@ -359,10 +370,10 @@ IMGUI_FUNCTION(CalcItemWidth) CALL_FUNCTION(CalcItemWidth, float) PUSH_NUMBER(ret) END_IMGUI_FUNC -// IMGUI_API void PushTextWrapPos(float wrap_pos_x = 0.0f); // word-wrapping for Text*() commands. < 0.0f: no wrapping; 0.0f: wrap to end of window (or column); > 0.0f: wrap at 'wrap_pos_x' position in window local space +// IMGUI_API void PushTextWrapPos(float wrap_local_pos_x = 0.0f); // word-wrapping for Text*() commands. < 0.0f: no wrapping; 0.0f: wrap to end of window (or column); > 0.0f: wrap at 'wrap_pos_x' position in window local space IMGUI_FUNCTION(PushTextWrapPos) -OPTIONAL_NUMBER_ARG(wrap_pos_x, 0.0f) -CALL_FUNCTION_NO_RET(PushTextWrapPos, wrap_pos_x) +OPTIONAL_NUMBER_ARG(wrap_local_pos_x, 0.0f) +CALL_FUNCTION_NO_RET(PushTextWrapPos, wrap_local_pos_x) END_IMGUI_FUNC // IMGUI_API void PopTextWrapPos(); IMGUI_FUNCTION(PopTextWrapPos) @@ -390,21 +401,21 @@ END_IMGUI_FUNC IMGUI_FUNCTION(Separator) CALL_FUNCTION_NO_RET(Separator) END_IMGUI_FUNC -// IMGUI_API void SameLine(float pos_x = 0.0f, float spacing_w = -1.0f); // call between widgets or groups to layout them horizontally +// IMGUI_API void SameLine(float local_pos_x = 0.0f, float spacing_w = -1.0f); // call between widgets or groups to layout them horizontally. X position given in window coordinates. IMGUI_FUNCTION(SameLine) -OPTIONAL_NUMBER_ARG(pos_x, 0.0f) +OPTIONAL_NUMBER_ARG(local_pos_x, 0.0f) OPTIONAL_NUMBER_ARG(spacing_w, -1.0f) -CALL_FUNCTION_NO_RET(SameLine, pos_x, spacing_w) +CALL_FUNCTION_NO_RET(SameLine, local_pos_x, spacing_w) END_IMGUI_FUNC -// IMGUI_API void NewLine(); // undo a SameLine() +// IMGUI_API void NewLine(); // undo a SameLine() or force a new line when in an horizontal-layout context. IMGUI_FUNCTION(NewLine) CALL_FUNCTION_NO_RET(NewLine) END_IMGUI_FUNC -// IMGUI_API void Spacing(); // add vertical spacing +// IMGUI_API void Spacing(); // add vertical spacing. IMGUI_FUNCTION(Spacing) CALL_FUNCTION_NO_RET(Spacing) END_IMGUI_FUNC -// IMGUI_API void Dummy(const ImVec2& size); // add a dummy item of given size +// IMGUI_API void Dummy(const ImVec2& size); // add a dummy item of given size. unlike InvisibleButton(), Dummy() won't take the mouse click or be navigable into. IMGUI_FUNCTION(Dummy) IM_VEC_2_ARG(size) CALL_FUNCTION_NO_RET(Dummy, size) @@ -419,48 +430,48 @@ IMGUI_FUNCTION(Unindent) OPTIONAL_NUMBER_ARG(indent_w, 0.0f) CALL_FUNCTION_NO_RET(Unindent, indent_w) END_IMGUI_FUNC -// IMGUI_API void BeginGroup(); // lock horizontal starting position + capture group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.) +// IMGUI_API void BeginGroup(); // lock horizontal starting position IMGUI_FUNCTION(BeginGroup) CALL_FUNCTION_NO_RET(BeginGroup) ADD_END_STACK(4) END_IMGUI_FUNC -// IMGUI_API void EndGroup(); +// IMGUI_API void EndGroup(); // unlock horizontal starting position + capture the whole group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.) IMGUI_FUNCTION(EndGroup) CALL_FUNCTION_NO_RET(EndGroup) POP_END_STACK(4) END_IMGUI_FUNC -// IMGUI_API ImVec2 GetCursorPos(); // cursor position is relative to window position +// IMGUI_API ImVec2 GetCursorPos(); // cursor position in window coordinates (relative to window position) IMGUI_FUNCTION(GetCursorPos) CALL_FUNCTION(GetCursorPos, ImVec2) PUSH_NUMBER(ret.x) PUSH_NUMBER(ret.y) END_IMGUI_FUNC -// IMGUI_API float GetCursorPosX(); // " +// IMGUI_API float GetCursorPosX(); // (some functions are using window-relative coordinates, such as: GetCursorPos, GetCursorStartPos, GetContentRegionMax, GetWindowContentRegion* etc. IMGUI_FUNCTION(GetCursorPosX) CALL_FUNCTION(GetCursorPosX, float) PUSH_NUMBER(ret) END_IMGUI_FUNC -// IMGUI_API float GetCursorPosY(); // " +// IMGUI_API float GetCursorPosY(); // other functions such as GetCursorScreenPos or everything in ImDrawList:: IMGUI_FUNCTION(GetCursorPosY) CALL_FUNCTION(GetCursorPosY, float) PUSH_NUMBER(ret) END_IMGUI_FUNC -// IMGUI_API void SetCursorPos(const ImVec2& local_pos); // " +// IMGUI_API void SetCursorPos(const ImVec2& local_pos); // are using the main, absolute coordinate system. IMGUI_FUNCTION(SetCursorPos) IM_VEC_2_ARG(local_pos) CALL_FUNCTION_NO_RET(SetCursorPos, local_pos) END_IMGUI_FUNC -// IMGUI_API void SetCursorPosX(float x); // " +// IMGUI_API void SetCursorPosX(float local_x); // GetWindowPos() + GetCursorPos() == GetCursorScreenPos() etc.) IMGUI_FUNCTION(SetCursorPosX) -NUMBER_ARG(x) -CALL_FUNCTION_NO_RET(SetCursorPosX, x) +NUMBER_ARG(local_x) +CALL_FUNCTION_NO_RET(SetCursorPosX, local_x) END_IMGUI_FUNC -// IMGUI_API void SetCursorPosY(float y); // " +// IMGUI_API void SetCursorPosY(float local_y); // IMGUI_FUNCTION(SetCursorPosY) -NUMBER_ARG(y) -CALL_FUNCTION_NO_RET(SetCursorPosY, y) +NUMBER_ARG(local_y) +CALL_FUNCTION_NO_RET(SetCursorPosY, local_y) END_IMGUI_FUNC -// IMGUI_API ImVec2 GetCursorStartPos(); // initial cursor position +// IMGUI_API ImVec2 GetCursorStartPos(); // initial cursor position in window coordinates IMGUI_FUNCTION(GetCursorStartPos) CALL_FUNCTION(GetCursorStartPos, ImVec2) PUSH_NUMBER(ret.x) @@ -472,10 +483,10 @@ CALL_FUNCTION(GetCursorScreenPos, ImVec2) PUSH_NUMBER(ret.x) PUSH_NUMBER(ret.y) END_IMGUI_FUNC -// IMGUI_API void SetCursorScreenPos(const ImVec2& screen_pos); // cursor position in absolute screen coordinates [0..io.DisplaySize] +// IMGUI_API void SetCursorScreenPos(const ImVec2& pos); // cursor position in absolute screen coordinates [0..io.DisplaySize] IMGUI_FUNCTION(SetCursorScreenPos) -IM_VEC_2_ARG(screen_pos) -CALL_FUNCTION_NO_RET(SetCursorScreenPos, screen_pos) +IM_VEC_2_ARG(pos) +CALL_FUNCTION_NO_RET(SetCursorScreenPos, pos) END_IMGUI_FUNC // IMGUI_API void AlignTextToFramePadding(); // vertically align upcoming text baseline to FramePadding.y so that it will align properly to regularly framed items (call if you have text on a line before a framed item) IMGUI_FUNCTION(AlignTextToFramePadding) @@ -501,25 +512,25 @@ IMGUI_FUNCTION(GetFrameHeightWithSpacing) CALL_FUNCTION(GetFrameHeightWithSpacing, float) PUSH_NUMBER(ret) END_IMGUI_FUNC -// IMGUI_API void PushID(const char* str_id); // push identifier into the ID stack. IDs are hash of the entire stack! +// IMGUI_API void PushID(const char* str_id); // push string into the ID stack (will hash string). IMGUI_FUNCTION(PushID) LABEL_ARG(str_id) CALL_FUNCTION_NO_RET(PushID, str_id) END_IMGUI_FUNC -// IMGUI_API void PushID(const char* str_id_begin, const char* str_id_end); +// IMGUI_API void PushID(const char* str_id_begin, const char* str_id_end); // push string into the ID stack (will hash string). IMGUI_FUNCTION(PushID_2) LABEL_ARG(str_id_begin) LABEL_ARG(str_id_end) CALL_FUNCTION_NO_RET(PushID, str_id_begin, str_id_end) END_IMGUI_FUNC -// IMGUI_API void PushID(const void* ptr_id); +// IMGUI_API void PushID(const void* ptr_id); // push pointer into the ID stack (will hash pointer). // Unsupported arg type const void* ptr_id -// IMGUI_API void PushID(int int_id); +// IMGUI_API void PushID(int int_id); // push integer into the ID stack (will hash integer). IMGUI_FUNCTION(PushID_1) INT_ARG(int_id) CALL_FUNCTION_NO_RET(PushID, int_id) END_IMGUI_FUNC -// IMGUI_API void PopID(); +// IMGUI_API void PopID(); // pop from the ID stack. IMGUI_FUNCTION(PopID) CALL_FUNCTION_NO_RET(PopID) END_IMGUI_FUNC @@ -581,15 +592,15 @@ LABEL_ARG(label) CALL_FUNCTION(SmallButton, bool, label) PUSH_BOOL(ret) END_IMGUI_FUNC -// IMGUI_API bool ArrowButton(const char* str_id, ImGuiDir dir); -// Unsupported arg type ImGuiDir dir -// IMGUI_API bool InvisibleButton(const char* str_id, const ImVec2& size); // button behavior without the visuals, useful to build custom behaviors using the public api (along with IsItemActive, IsItemHovered, etc.) +// IMGUI_API bool InvisibleButton(const char* str_id, const ImVec2& size); // button behavior without the visuals, frequently useful to build custom behaviors using the public api (along with IsItemActive, IsItemHovered, etc.) IMGUI_FUNCTION(InvisibleButton) LABEL_ARG(str_id) IM_VEC_2_ARG(size) CALL_FUNCTION(InvisibleButton, bool, str_id, size) PUSH_BOOL(ret) END_IMGUI_FUNC +// IMGUI_API bool ArrowButton(const char* str_id, ImGuiDir dir); // square button with an arrow shape +// Unsupported arg type ImGuiDir dir // IMGUI_API void Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2 0 0, const ImVec2& uv1 = ImVec2 1 1, const ImVec4& tint_col = ImVec4 1 1 1 1, const ImVec4& border_col = ImVec4 0 0 0 0); IMGUI_FUNCTION(Image) IM_TEXTURE_ID_ARG(user_texture_id) @@ -629,14 +640,14 @@ CALL_FUNCTION(CheckboxFlags, bool, label, flags, flags_value) PUSH_BOOL(ret) END_UINT_POINTER(flags) END_IMGUI_FUNC -// IMGUI_API bool RadioButton(const char* label, bool active); +// IMGUI_API bool RadioButton(const char* label, bool active); // use with e.g. if (RadioButton("one", my_value==1)) { my_value = 1; } IMGUI_FUNCTION(RadioButton) LABEL_ARG(label) BOOL_ARG(active) CALL_FUNCTION(RadioButton, bool, label, active) PUSH_BOOL(ret) END_IMGUI_FUNC -// IMGUI_API bool RadioButton(const char* label, int* v, int v_button); +// IMGUI_API bool RadioButton(const char* label, int* v, int v_button); // shortcut to handle the above pattern when value is an integer IMGUI_FUNCTION(RadioButton_3) LABEL_ARG(label) INT_POINTER_ARG(v) @@ -645,20 +656,6 @@ CALL_FUNCTION(RadioButton, bool, label, v, v_button) PUSH_BOOL(ret) END_INT_POINTER(v) END_IMGUI_FUNC -// IMGUI_API void PlotLines(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2 0 0, int stride = sizeof(float)); -// Unsupported arg type const float* values -// Unsupported arg type ImVec2 graph_size = ImVec2 0 0 -// IMGUI_API void PlotLines(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2 0 0); -// Unsupported arg type float (*values_getter)(void* data -// Unsupported arg type void* data -// Unsupported arg type ImVec2 graph_size = ImVec2 0 0 -// IMGUI_API void PlotHistogram(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2 0 0, int stride = sizeof(float)); -// Unsupported arg type const float* values -// Unsupported arg type ImVec2 graph_size = ImVec2 0 0 -// IMGUI_API void PlotHistogram(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2 0 0); -// Unsupported arg type float (*values_getter)(void* data -// Unsupported arg type void* data -// Unsupported arg type ImVec2 graph_size = ImVec2 0 0 // IMGUI_API void ProgressBar(float fraction, const ImVec2& size_arg = ImVec2 -1 0, const char* overlay = NULL); IMGUI_FUNCTION(ProgressBar) NUMBER_ARG(fraction) @@ -693,26 +690,26 @@ END_IMGUI_FUNC // Unsupported arg type bool(*items_getter)(void* data // Unsupported arg type const char** out_text) // Unsupported arg type void* data -// IMGUI_API bool DragFloat(const char* label, float* v, float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", float power = 1.0f); // If v_min >= v_max we have no bound +// IMGUI_API bool DragFloat(const char* label, float* v, float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", float power = 1.0f); // If v_min >= v_max we have no bound IMGUI_FUNCTION(DragFloat) LABEL_ARG(label) FLOAT_POINTER_ARG(v) OPTIONAL_NUMBER_ARG(v_speed, 1.0f) OPTIONAL_NUMBER_ARG(v_min, 0.0f) OPTIONAL_NUMBER_ARG(v_max, 0.0f) -LABEL_ARG(display_format) +LABEL_ARG(format) OPTIONAL_NUMBER_ARG(power, 1.0f) -CALL_FUNCTION(DragFloat, bool, label, v, v_speed, v_min, v_max, display_format, power) +CALL_FUNCTION(DragFloat, bool, label, v, v_speed, v_min, v_max, format, power) PUSH_BOOL(ret) END_FLOAT_POINTER(v) END_IMGUI_FUNC -// IMGUI_API bool DragFloat2(const char* label, float v[2], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", float power = 1.0f); +// IMGUI_API bool DragFloat2(const char* label, float v[2], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", float power = 1.0f); // Unsupported arg type float v[2] -// IMGUI_API bool DragFloat3(const char* label, float v[3], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", float power = 1.0f); +// IMGUI_API bool DragFloat3(const char* label, float v[3], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", float power = 1.0f); // Unsupported arg type float v[3] -// IMGUI_API bool DragFloat4(const char* label, float v[4], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", float power = 1.0f); +// IMGUI_API bool DragFloat4(const char* label, float v[4], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", float power = 1.0f); // Unsupported arg type float v[4] -// IMGUI_API bool DragFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", const char* display_format_max = NULL, float power = 1.0f); +// IMGUI_API bool DragFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", const char* format_max = NULL, float power = 1.0f); IMGUI_FUNCTION(DragFloatRange2) LABEL_ARG(label) FLOAT_POINTER_ARG(v_current_min) @@ -720,33 +717,33 @@ FLOAT_POINTER_ARG(v_current_max) OPTIONAL_NUMBER_ARG(v_speed, 1.0f) OPTIONAL_NUMBER_ARG(v_min, 0.0f) OPTIONAL_NUMBER_ARG(v_max, 0.0f) -LABEL_ARG(display_format) -OPTIONAL_LABEL_ARG(display_format_max) +LABEL_ARG(format) +OPTIONAL_LABEL_ARG(format_max) OPTIONAL_NUMBER_ARG(power, 1.0f) -CALL_FUNCTION(DragFloatRange2, bool, label, v_current_min, v_current_max, v_speed, v_min, v_max, display_format, display_format_max, power) +CALL_FUNCTION(DragFloatRange2, bool, label, v_current_min, v_current_max, v_speed, v_min, v_max, format, format_max, power) PUSH_BOOL(ret) END_FLOAT_POINTER(v_current_min) END_FLOAT_POINTER(v_current_max) END_IMGUI_FUNC -// IMGUI_API bool DragInt(const char* label, int* v, float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* display_format = "%.0f"); // If v_min >= v_max we have no bound +// IMGUI_API bool DragInt(const char* label, int* v, float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d"); // If v_min >= v_max we have no bound IMGUI_FUNCTION(DragInt) LABEL_ARG(label) INT_POINTER_ARG(v) OPTIONAL_NUMBER_ARG(v_speed, 1.0f) OPTIONAL_INT_ARG(v_min, 0) OPTIONAL_INT_ARG(v_max, 0) -LABEL_ARG(display_format) -CALL_FUNCTION(DragInt, bool, label, v, v_speed, v_min, v_max, display_format) +LABEL_ARG(format) +CALL_FUNCTION(DragInt, bool, label, v, v_speed, v_min, v_max, format) PUSH_BOOL(ret) END_INT_POINTER(v) END_IMGUI_FUNC -// IMGUI_API bool DragInt2(const char* label, int v[2], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* display_format = "%.0f"); +// IMGUI_API bool DragInt2(const char* label, int v[2], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d"); // Unsupported arg type int v[2] -// IMGUI_API bool DragInt3(const char* label, int v[3], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* display_format = "%.0f"); +// IMGUI_API bool DragInt3(const char* label, int v[3], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d"); // Unsupported arg type int v[3] -// IMGUI_API bool DragInt4(const char* label, int v[4], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* display_format = "%.0f"); +// IMGUI_API bool DragInt4(const char* label, int v[4], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d"); // Unsupported arg type int v[4] -// IMGUI_API bool DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* display_format = "%.0f", const char* display_format_max = NULL); +// IMGUI_API bool DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d", const char* format_max = NULL); IMGUI_FUNCTION(DragIntRange2) LABEL_ARG(label) INT_POINTER_ARG(v_current_min) @@ -754,132 +751,173 @@ INT_POINTER_ARG(v_current_max) OPTIONAL_NUMBER_ARG(v_speed, 1.0f) OPTIONAL_INT_ARG(v_min, 0) OPTIONAL_INT_ARG(v_max, 0) -LABEL_ARG(display_format) -OPTIONAL_LABEL_ARG(display_format_max) -CALL_FUNCTION(DragIntRange2, bool, label, v_current_min, v_current_max, v_speed, v_min, v_max, display_format, display_format_max) +LABEL_ARG(format) +OPTIONAL_LABEL_ARG(format_max) +CALL_FUNCTION(DragIntRange2, bool, label, v_current_min, v_current_max, v_speed, v_min, v_max, format, format_max) PUSH_BOOL(ret) END_INT_POINTER(v_current_min) END_INT_POINTER(v_current_max) END_IMGUI_FUNC -// IMGUI_API bool InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, ImGuiTextEditCallback callback = NULL, void* user_data = NULL); -// Unsupported arg type char* buf -// Unsupported arg type size_t buf_size -// Unsupported arg type ImGuiTextEditCallback callback = NULL -// Unsupported arg type void* user_data = NULL -// IMGUI_API bool InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size = ImVec2 0 0, ImGuiInputTextFlags flags = 0, ImGuiTextEditCallback callback = NULL, void* user_data = NULL); -// Unsupported arg type char* buf -// Unsupported arg type size_t buf_size -// Unsupported arg type ImGuiTextEditCallback callback = NULL -// Unsupported arg type void* user_data = NULL -// IMGUI_API bool InputFloat(const char* label, float* v, float step = 0.0f, float step_fast = 0.0f, int decimal_precision = -1, ImGuiInputTextFlags extra_flags = 0); -IMGUI_FUNCTION(InputFloat) -LABEL_ARG(label) -FLOAT_POINTER_ARG(v) -OPTIONAL_NUMBER_ARG(step, 0.0f) -OPTIONAL_NUMBER_ARG(step_fast, 0.0f) -OPTIONAL_INT_ARG(decimal_precision, -1) -OPTIONAL_INT_ARG(extra_flags, 0) -CALL_FUNCTION(InputFloat, bool, label, v, step, step_fast, decimal_precision, extra_flags) -PUSH_BOOL(ret) -END_FLOAT_POINTER(v) -END_IMGUI_FUNC -// IMGUI_API bool InputFloat2(const char* label, float v[2], int decimal_precision = -1, ImGuiInputTextFlags extra_flags = 0); -// Unsupported arg type float v[2] -// IMGUI_API bool InputFloat3(const char* label, float v[3], int decimal_precision = -1, ImGuiInputTextFlags extra_flags = 0); -// Unsupported arg type float v[3] -// IMGUI_API bool InputFloat4(const char* label, float v[4], int decimal_precision = -1, ImGuiInputTextFlags extra_flags = 0); -// Unsupported arg type float v[4] -// IMGUI_API bool InputInt(const char* label, int* v, int step = 1, int step_fast = 100, ImGuiInputTextFlags extra_flags = 0); -IMGUI_FUNCTION(InputInt) -LABEL_ARG(label) -INT_POINTER_ARG(v) -OPTIONAL_INT_ARG(step, 1) -OPTIONAL_INT_ARG(step_fast, 100) -OPTIONAL_INT_ARG(extra_flags, 0) -CALL_FUNCTION(InputInt, bool, label, v, step, step_fast, extra_flags) -PUSH_BOOL(ret) -END_INT_POINTER(v) -END_IMGUI_FUNC -// IMGUI_API bool InputInt2(const char* label, int v[2], ImGuiInputTextFlags extra_flags = 0); -// Unsupported arg type int v[2] -// IMGUI_API bool InputInt3(const char* label, int v[3], ImGuiInputTextFlags extra_flags = 0); -// Unsupported arg type int v[3] -// IMGUI_API bool InputInt4(const char* label, int v[4], ImGuiInputTextFlags extra_flags = 0); -// Unsupported arg type int v[4] -// IMGUI_API bool InputDouble(const char* label, double* v, double step = 0.0f, double step_fast = 0.0f, const char* display_format = "%.6f", ImGuiInputTextFlags extra_flags = 0); -// Unsupported arg type double* v -// Unsupported arg type double step = 0.0f -// Unsupported arg type double step_fast = 0.0f -// IMGUI_API bool SliderFloat(const char* label, float* v, float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); // adjust display_format to decorate the value with a prefix or a suffix for in-slider labels or unit display. Use power!=1.0 for logarithmic sliders +// IMGUI_API bool DragScalar(const char* label, ImGuiDataType data_type, void* v, float v_speed, const void* v_min = NULL, const void* v_max = NULL, const char* format = NULL, float power = 1.0f); +// Unsupported arg type ImGuiDataType data_type +// Unsupported arg type void* v +// Unsupported arg type const void* v_min = NULL +// Unsupported arg type const void* v_max = NULL +// IMGUI_API bool DragScalarN(const char* label, ImGuiDataType data_type, void* v, int components, float v_speed, const void* v_min = NULL, const void* v_max = NULL, const char* format = NULL, float power = 1.0f); +// Unsupported arg type ImGuiDataType data_type +// Unsupported arg type void* v +// Unsupported arg type const void* v_min = NULL +// Unsupported arg type const void* v_max = NULL +// IMGUI_API bool SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f); // adjust format to decorate the value with a prefix or a suffix for in-slider labels or unit display. Use power!=1.0 for power curve sliders IMGUI_FUNCTION(SliderFloat) LABEL_ARG(label) FLOAT_POINTER_ARG(v) NUMBER_ARG(v_min) NUMBER_ARG(v_max) -LABEL_ARG(display_format) +LABEL_ARG(format) OPTIONAL_NUMBER_ARG(power, 1.0f) -CALL_FUNCTION(SliderFloat, bool, label, v, v_min, v_max, display_format, power) +CALL_FUNCTION(SliderFloat, bool, label, v, v_min, v_max, format, power) PUSH_BOOL(ret) END_FLOAT_POINTER(v) END_IMGUI_FUNC -// IMGUI_API bool SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); +// IMGUI_API bool SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format = "%.3f", float power = 1.0f); // Unsupported arg type float v[2] -// IMGUI_API bool SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); +// IMGUI_API bool SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format = "%.3f", float power = 1.0f); // Unsupported arg type float v[3] -// IMGUI_API bool SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); +// IMGUI_API bool SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format = "%.3f", float power = 1.0f); // Unsupported arg type float v[4] -// IMGUI_API bool SliderAngle(const char* label, float* v_rad, float v_degrees_min = -360.0f, float v_degrees_max = +360.0f); +// IMGUI_API bool SliderAngle(const char* label, float* v_rad, float v_degrees_min = -360.0f, float v_degrees_max = +360.0f, const char* format = "%.0f deg"); IMGUI_FUNCTION(SliderAngle) LABEL_ARG(label) FLOAT_POINTER_ARG(v_rad) OPTIONAL_NUMBER_ARG(v_degrees_min, -360.0f) OPTIONAL_NUMBER_ARG(v_degrees_max, +360.0f) -CALL_FUNCTION(SliderAngle, bool, label, v_rad, v_degrees_min, v_degrees_max) +LABEL_ARG(format) +CALL_FUNCTION(SliderAngle, bool, label, v_rad, v_degrees_min, v_degrees_max, format) PUSH_BOOL(ret) END_FLOAT_POINTER(v_rad) END_IMGUI_FUNC -// IMGUI_API bool SliderInt(const char* label, int* v, int v_min, int v_max, const char* display_format = "%.0f"); +// IMGUI_API bool SliderInt(const char* label, int* v, int v_min, int v_max, const char* format = "%d"); IMGUI_FUNCTION(SliderInt) LABEL_ARG(label) INT_POINTER_ARG(v) INT_ARG(v_min) INT_ARG(v_max) -LABEL_ARG(display_format) -CALL_FUNCTION(SliderInt, bool, label, v, v_min, v_max, display_format) +LABEL_ARG(format) +CALL_FUNCTION(SliderInt, bool, label, v, v_min, v_max, format) PUSH_BOOL(ret) END_INT_POINTER(v) END_IMGUI_FUNC -// IMGUI_API bool SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* display_format = "%.0f"); +// IMGUI_API bool SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* format = "%d"); // Unsupported arg type int v[2] -// IMGUI_API bool SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* display_format = "%.0f"); +// IMGUI_API bool SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* format = "%d"); // Unsupported arg type int v[3] -// IMGUI_API bool SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* display_format = "%.0f"); +// IMGUI_API bool SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* format = "%d"); // Unsupported arg type int v[4] -// IMGUI_API bool VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); +// IMGUI_API bool SliderScalar(const char* label, ImGuiDataType data_type, void* v, const void* v_min, const void* v_max, const char* format = NULL, float power = 1.0f); +// Unsupported arg type ImGuiDataType data_type +// Unsupported arg type void* v +// Unsupported arg type const void* v_min +// Unsupported arg type const void* v_max +// IMGUI_API bool SliderScalarN(const char* label, ImGuiDataType data_type, void* v, int components, const void* v_min, const void* v_max, const char* format = NULL, float power = 1.0f); +// Unsupported arg type ImGuiDataType data_type +// Unsupported arg type void* v +// Unsupported arg type const void* v_min +// Unsupported arg type const void* v_max +// IMGUI_API bool VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f); IMGUI_FUNCTION(VSliderFloat) LABEL_ARG(label) IM_VEC_2_ARG(size) FLOAT_POINTER_ARG(v) NUMBER_ARG(v_min) NUMBER_ARG(v_max) -LABEL_ARG(display_format) +LABEL_ARG(format) OPTIONAL_NUMBER_ARG(power, 1.0f) -CALL_FUNCTION(VSliderFloat, bool, label, size, v, v_min, v_max, display_format, power) +CALL_FUNCTION(VSliderFloat, bool, label, size, v, v_min, v_max, format, power) PUSH_BOOL(ret) END_FLOAT_POINTER(v) END_IMGUI_FUNC -// IMGUI_API bool VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* display_format = "%.0f"); +// IMGUI_API bool VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* format = "%d"); IMGUI_FUNCTION(VSliderInt) LABEL_ARG(label) IM_VEC_2_ARG(size) INT_POINTER_ARG(v) INT_ARG(v_min) INT_ARG(v_max) -LABEL_ARG(display_format) -CALL_FUNCTION(VSliderInt, bool, label, size, v, v_min, v_max, display_format) +LABEL_ARG(format) +CALL_FUNCTION(VSliderInt, bool, label, size, v, v_min, v_max, format) +PUSH_BOOL(ret) +END_INT_POINTER(v) +END_IMGUI_FUNC +// IMGUI_API bool VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType data_type, void* v, const void* v_min, const void* v_max, const char* format = NULL, float power = 1.0f); +// Unsupported arg type ImGuiDataType data_type +// Unsupported arg type void* v +// Unsupported arg type const void* v_min +// Unsupported arg type const void* v_max +// IMGUI_API bool InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); +// Unsupported arg type char* buf +// Unsupported arg type size_t buf_size +// Unsupported arg type ImGuiInputTextCallback callback = NULL +// Unsupported arg type void* user_data = NULL +// IMGUI_API bool InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size = ImVec2 0 0, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); +// Unsupported arg type char* buf +// Unsupported arg type size_t buf_size +// Unsupported arg type ImGuiInputTextCallback callback = NULL +// Unsupported arg type void* user_data = NULL +// IMGUI_API bool InputTextWithHint(const char* label, const char* hint, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); +// Unsupported arg type char* buf +// Unsupported arg type size_t buf_size +// Unsupported arg type ImGuiInputTextCallback callback = NULL +// Unsupported arg type void* user_data = NULL +// IMGUI_API bool InputFloat(const char* label, float* v, float step = 0.0f, float step_fast = 0.0f, const char* format = "%.3f", ImGuiInputTextFlags flags = 0); +IMGUI_FUNCTION(InputFloat) +LABEL_ARG(label) +FLOAT_POINTER_ARG(v) +OPTIONAL_NUMBER_ARG(step, 0.0f) +OPTIONAL_NUMBER_ARG(step_fast, 0.0f) +LABEL_ARG(format) +OPTIONAL_INT_ARG(flags, 0) +CALL_FUNCTION(InputFloat, bool, label, v, step, step_fast, format, flags) +PUSH_BOOL(ret) +END_FLOAT_POINTER(v) +END_IMGUI_FUNC +// IMGUI_API bool InputFloat2(const char* label, float v[2], const char* format = "%.3f", ImGuiInputTextFlags flags = 0); +// Unsupported arg type float v[2] +// IMGUI_API bool InputFloat3(const char* label, float v[3], const char* format = "%.3f", ImGuiInputTextFlags flags = 0); +// Unsupported arg type float v[3] +// IMGUI_API bool InputFloat4(const char* label, float v[4], const char* format = "%.3f", ImGuiInputTextFlags flags = 0); +// Unsupported arg type float v[4] +// IMGUI_API bool InputInt(const char* label, int* v, int step = 1, int step_fast = 100, ImGuiInputTextFlags flags = 0); +IMGUI_FUNCTION(InputInt) +LABEL_ARG(label) +INT_POINTER_ARG(v) +OPTIONAL_INT_ARG(step, 1) +OPTIONAL_INT_ARG(step_fast, 100) +OPTIONAL_INT_ARG(flags, 0) +CALL_FUNCTION(InputInt, bool, label, v, step, step_fast, flags) PUSH_BOOL(ret) END_INT_POINTER(v) END_IMGUI_FUNC +// IMGUI_API bool InputInt2(const char* label, int v[2], ImGuiInputTextFlags flags = 0); +// Unsupported arg type int v[2] +// IMGUI_API bool InputInt3(const char* label, int v[3], ImGuiInputTextFlags flags = 0); +// Unsupported arg type int v[3] +// IMGUI_API bool InputInt4(const char* label, int v[4], ImGuiInputTextFlags flags = 0); +// Unsupported arg type int v[4] +// IMGUI_API bool InputDouble(const char* label, double* v, double step = 0.0, double step_fast = 0.0, const char* format = "%.6f", ImGuiInputTextFlags flags = 0); +// Unsupported arg type double* v +// Unsupported arg type double step = 0.0 +// Unsupported arg type double step_fast = 0.0 +// IMGUI_API bool InputScalar(const char* label, ImGuiDataType data_type, void* v, const void* step = NULL, const void* step_fast = NULL, const char* format = NULL, ImGuiInputTextFlags flags = 0); +// Unsupported arg type ImGuiDataType data_type +// Unsupported arg type void* v +// Unsupported arg type const void* step = NULL +// Unsupported arg type const void* step_fast = NULL +// IMGUI_API bool InputScalarN(const char* label, ImGuiDataType data_type, void* v, int components, const void* step = NULL, const void* step_fast = NULL, const char* format = NULL, ImGuiInputTextFlags flags = 0); +// Unsupported arg type ImGuiDataType data_type +// Unsupported arg type void* v +// Unsupported arg type const void* step = NULL +// Unsupported arg type const void* step_fast = NULL // IMGUI_API bool ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flags = 0); // Unsupported arg type float col[3] // Unsupported arg type ImGuiColorEditFlags flags = 0 @@ -898,14 +936,14 @@ END_IMGUI_FUNC // Unsupported arg type ImVec2 size = ImVec2 0 0 // IMGUI_API void SetColorEditOptions(ImGuiColorEditFlags flags); // initialize current options (generally on application startup) if you want to select a default format, picker type, etc. User will be able to change many settings, unless you pass the _NoOptions flag to your calls. // Unsupported arg type ImGuiColorEditFlags flags -// IMGUI_API bool TreeNode(const char* label); // if returning 'true' the node is open and the tree id is pushed into the id stack. user is responsible for calling TreePop(). +// IMGUI_API bool TreeNode(const char* label); IMGUI_FUNCTION(TreeNode) LABEL_ARG(label) CALL_FUNCTION(TreeNode, bool, label) IF_RET_ADD_END_STACK(6) PUSH_BOOL(ret) END_IMGUI_FUNC -// IMGUI_API bool TreeNode(const char* str_id, const char* fmt, ...) IM_FMTARGS(2); // read the FAQ about why and how to use ID. to align arbitrary text at the same level as a TreeNode() you can use Bullet(). +// IMGUI_API bool TreeNode(const char* str_id, const char* fmt, ...) IM_FMTARGS(2); // helper variation to easily decorelate the id from the displayed string. Read the FAQ about why and how to use ID. to align arbitrary text at the same level as a TreeNode() you can use Bullet(). // Unsupported arg type ...) IM_FMTARGS(2 // IMGUI_API bool TreeNode(const void* ptr_id, const char* fmt, ...) IM_FMTARGS(2); // " // Unsupported arg type const void* ptr_id @@ -931,7 +969,7 @@ END_IMGUI_FUNC // Unsupported arg type const void* ptr_id // Unsupported arg type ImGuiTreeNodeFlags flags // Unsupported arg type va_list args) IM_FMTLIST(3 -// IMGUI_API void TreePush(const char* str_id); // ~ Indent()+PushId(). Already called by TreeNode() when returning true, but you can call Push/Pop yourself for layout purpose +// IMGUI_API void TreePush(const char* str_id); // ~ Indent()+PushId(). Already called by TreeNode() when returning true, but you can call TreePush/TreePop yourself if desired. IMGUI_FUNCTION(TreePush) LABEL_ARG(str_id) CALL_FUNCTION_NO_RET(TreePush, str_id) @@ -984,7 +1022,7 @@ END_IMGUI_FUNC // Unsupported arg type bool (*items_getter)(void* data // Unsupported arg type const char** out_text) // Unsupported arg type void* data -// IMGUI_API bool ListBoxHeader(const char* label, const ImVec2& size = ImVec2 0 0); // use if you want to reimplement ListBox() will custom data or interactions. make sure to call ListBoxFooter() afterwards. +// IMGUI_API bool ListBoxHeader(const char* label, const ImVec2& size = ImVec2 0 0); // use if you want to reimplement ListBox() will custom data or interactions. if the function return true, you can output elements then call ListBoxFooter() afterwards. IMGUI_FUNCTION(ListBoxHeader) LABEL_ARG(label) OPTIONAL_IM_VEC_2_ARG(size, 0, 0) @@ -999,10 +1037,24 @@ OPTIONAL_INT_ARG(height_in_items, -1) CALL_FUNCTION(ListBoxHeader, bool, label, items_count, height_in_items) PUSH_BOOL(ret) END_IMGUI_FUNC -// IMGUI_API void ListBoxFooter(); // terminate the scrolling region +// IMGUI_API void ListBoxFooter(); // terminate the scrolling region. only call ListBoxFooter() if ListBoxHeader() returned true! IMGUI_FUNCTION(ListBoxFooter) CALL_FUNCTION_NO_RET(ListBoxFooter) END_IMGUI_FUNC +// IMGUI_API void PlotLines(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2 0 0, int stride = sizeof(float)); +// Unsupported arg type const float* values +// Unsupported arg type ImVec2 graph_size = ImVec2 0 0 +// IMGUI_API void PlotLines(const char* label, float(*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2 0 0); +// Unsupported arg type float(*values_getter)(void* data +// Unsupported arg type void* data +// Unsupported arg type ImVec2 graph_size = ImVec2 0 0 +// IMGUI_API void PlotHistogram(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2 0 0, int stride = sizeof(float)); +// Unsupported arg type const float* values +// Unsupported arg type ImVec2 graph_size = ImVec2 0 0 +// IMGUI_API void PlotHistogram(const char* label, float(*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2 0 0); +// Unsupported arg type float(*values_getter)(void* data +// Unsupported arg type void* data +// Unsupported arg type ImVec2 graph_size = ImVec2 0 0 // IMGUI_API void Value(const char* prefix, bool b); IMGUI_FUNCTION(Value) LABEL_ARG(prefix) @@ -1028,54 +1080,40 @@ NUMBER_ARG(v) OPTIONAL_LABEL_ARG(float_format) CALL_FUNCTION_NO_RET(Value, prefix, v, float_format) END_IMGUI_FUNC -// IMGUI_API void SetTooltip(const char* fmt, ...) IM_FMTARGS(1); // set text tooltip under mouse-cursor, typically use with ImGui::IsItemHovered(). overidde any previous call to SetTooltip(). -// Unsupported arg type ...) IM_FMTARGS(1 -// IMGUI_API void SetTooltipV(const char* fmt, va_list args) IM_FMTLIST(1); -// Unsupported arg type va_list args) IM_FMTLIST(1 -// IMGUI_API void BeginTooltip(); // begin/append a tooltip window. to create full-featured tooltip (with any kind of contents). -IMGUI_FUNCTION(BeginTooltip) -CALL_FUNCTION_NO_RET(BeginTooltip) -ADD_END_STACK(7) -END_IMGUI_FUNC -// IMGUI_API void EndTooltip(); -IMGUI_FUNCTION(EndTooltip) -CALL_FUNCTION_NO_RET(EndTooltip) -POP_END_STACK(7) -END_IMGUI_FUNC // IMGUI_API bool BeginMainMenuBar(); // create and append to a full screen menu-bar. IMGUI_FUNCTION(BeginMainMenuBar) CALL_FUNCTION(BeginMainMenuBar, bool) -IF_RET_ADD_END_STACK(8) +IF_RET_ADD_END_STACK(7) PUSH_BOOL(ret) END_IMGUI_FUNC // IMGUI_API void EndMainMenuBar(); // only call EndMainMenuBar() if BeginMainMenuBar() returns true! IMGUI_FUNCTION(EndMainMenuBar) CALL_FUNCTION_NO_RET(EndMainMenuBar) -POP_END_STACK(8) +POP_END_STACK(7) END_IMGUI_FUNC // IMGUI_API bool BeginMenuBar(); // append to menu-bar of current window (requires ImGuiWindowFlags_MenuBar flag set on parent window). IMGUI_FUNCTION(BeginMenuBar) CALL_FUNCTION(BeginMenuBar, bool) -IF_RET_ADD_END_STACK(9) +IF_RET_ADD_END_STACK(8) PUSH_BOOL(ret) END_IMGUI_FUNC // IMGUI_API void EndMenuBar(); // only call EndMenuBar() if BeginMenuBar() returns true! IMGUI_FUNCTION(EndMenuBar) CALL_FUNCTION_NO_RET(EndMenuBar) -POP_END_STACK(9) +POP_END_STACK(8) END_IMGUI_FUNC // IMGUI_API bool BeginMenu(const char* label, bool enabled = true); // create a sub-menu entry. only call EndMenu() if this returns true! IMGUI_FUNCTION(BeginMenu) LABEL_ARG(label) OPTIONAL_BOOL_ARG(enabled, true) CALL_FUNCTION(BeginMenu, bool, label, enabled) -IF_RET_ADD_END_STACK(10) +IF_RET_ADD_END_STACK(9) PUSH_BOOL(ret) END_IMGUI_FUNC // IMGUI_API void EndMenu(); // only call EndMenu() if BeginMenu() returns true! IMGUI_FUNCTION(EndMenu) CALL_FUNCTION_NO_RET(EndMenu) -POP_END_STACK(10) +POP_END_STACK(9) END_IMGUI_FUNC // IMGUI_API bool MenuItem(const char* label, const char* shortcut = NULL, bool selected = false, bool enabled = true); // return true when activated. shortcuts are displayed for convenience but not processed by ImGui at the moment IMGUI_FUNCTION(MenuItem) @@ -1096,6 +1134,20 @@ CALL_FUNCTION(MenuItem, bool, label, shortcut, p_selected, enabled) PUSH_BOOL(ret) END_BOOL_POINTER(p_selected) END_IMGUI_FUNC +// IMGUI_API void BeginTooltip(); // begin/append a tooltip window. to create full-featured tooltip (with any kind of items). +IMGUI_FUNCTION(BeginTooltip) +CALL_FUNCTION_NO_RET(BeginTooltip) +ADD_END_STACK(10) +END_IMGUI_FUNC +// IMGUI_API void EndTooltip(); +IMGUI_FUNCTION(EndTooltip) +CALL_FUNCTION_NO_RET(EndTooltip) +POP_END_STACK(10) +END_IMGUI_FUNC +// IMGUI_API void SetTooltip(const char* fmt, ...) IM_FMTARGS(1); // set a text-only tooltip, typically use with ImGui::IsItemHovered(). override any previous call to SetTooltip(). +// Unsupported arg type ...) IM_FMTARGS(1 +// IMGUI_API void SetTooltipV(const char* fmt, va_list args) IM_FMTLIST(1); +// Unsupported arg type va_list args) IM_FMTLIST(1 // IMGUI_API void OpenPopup(const char* str_id); // call to mark popup as open (don't call every frame!). popups are closed when user click outside, or if CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block. By default, Selectable()/MenuItem() are calling CloseCurrentPopup(). Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level). IMGUI_FUNCTION(OpenPopup) LABEL_ARG(str_id) @@ -1149,14 +1201,14 @@ IMGUI_FUNCTION(EndPopup) CALL_FUNCTION_NO_RET(EndPopup) POP_END_STACK(11) END_IMGUI_FUNC -// IMGUI_API bool OpenPopupOnItemClick(const char* str_id = NULL, int mouse_button = 1); // helper to open popup when clicked on last item. return true when just opened. +// IMGUI_API bool OpenPopupOnItemClick(const char* str_id = NULL, int mouse_button = 1); // helper to open popup when clicked on last item (note: actually triggers on the mouse _released_ event to be consistent with popup behaviors). return true when just opened. IMGUI_FUNCTION(OpenPopupOnItemClick) OPTIONAL_LABEL_ARG(str_id) OPTIONAL_INT_ARG(mouse_button, 1) CALL_FUNCTION(OpenPopupOnItemClick, bool, str_id, mouse_button) PUSH_BOOL(ret) END_IMGUI_FUNC -// IMGUI_API bool IsPopupOpen(const char* str_id); // return true if the popup is open +// IMGUI_API bool IsPopupOpen(const char* str_id); // return true if the popup is open at the current begin-ed level of the popup stack. IMGUI_FUNCTION(IsPopupOpen) LABEL_ARG(str_id) CALL_FUNCTION(IsPopupOpen, bool, str_id) @@ -1205,21 +1257,40 @@ CALL_FUNCTION_NO_RET(SetColumnOffset, column_index, offset_x) END_IMGUI_FUNC // IMGUI_API int GetColumnsCount(); // Unsupported return type int -// IMGUI_API void LogToTTY(int max_depth = -1); // start logging to tty +// IMGUI_API bool BeginTabBar(const char* str_id, ImGuiTabBarFlags flags = 0); // create and append into a TabBar +// Unsupported arg type ImGuiTabBarFlags flags = 0 +// IMGUI_API void EndTabBar(); // only call EndTabBar() if BeginTabBar() returns true! +IMGUI_FUNCTION(EndTabBar) +CALL_FUNCTION_NO_RET(EndTabBar) +POP_END_STACK(12) +END_IMGUI_FUNC +// IMGUI_API bool BeginTabItem(const char* label, bool* p_open = NULL, ImGuiTabItemFlags flags = 0);// create a Tab. Returns true if the Tab is selected. +// Unsupported arg type ImGuiTabItemFlags flags = 0 +// IMGUI_API void EndTabItem(); // only call EndTabItem() if BeginTabItem() returns true! +IMGUI_FUNCTION(EndTabItem) +CALL_FUNCTION_NO_RET(EndTabItem) +POP_END_STACK(13) +END_IMGUI_FUNC +// IMGUI_API void SetTabItemClosed(const char* tab_or_docked_window_label); // notify TabBar or Docking system of a closed tab/window ahead (useful to reduce visual flicker on reorderable tab bars). For tab-bar: call after BeginTabBar() and before Tab submissions. Otherwise call with a window name. +IMGUI_FUNCTION(SetTabItemClosed) +LABEL_ARG(tab_or_docked_window_label) +CALL_FUNCTION_NO_RET(SetTabItemClosed, tab_or_docked_window_label) +END_IMGUI_FUNC +// IMGUI_API void LogToTTY(int auto_open_depth = -1); // start logging to tty (stdout) IMGUI_FUNCTION(LogToTTY) -OPTIONAL_INT_ARG(max_depth, -1) -CALL_FUNCTION_NO_RET(LogToTTY, max_depth) +OPTIONAL_INT_ARG(auto_open_depth, -1) +CALL_FUNCTION_NO_RET(LogToTTY, auto_open_depth) END_IMGUI_FUNC -// IMGUI_API void LogToFile(int max_depth = -1, const char* filename = NULL); // start logging to file +// IMGUI_API void LogToFile(int auto_open_depth = -1, const char* filename = NULL); // start logging to file IMGUI_FUNCTION(LogToFile) -OPTIONAL_INT_ARG(max_depth, -1) +OPTIONAL_INT_ARG(auto_open_depth, -1) OPTIONAL_LABEL_ARG(filename) -CALL_FUNCTION_NO_RET(LogToFile, max_depth, filename) +CALL_FUNCTION_NO_RET(LogToFile, auto_open_depth, filename) END_IMGUI_FUNC -// IMGUI_API void LogToClipboard(int max_depth = -1); // start logging to OS clipboard +// IMGUI_API void LogToClipboard(int auto_open_depth = -1); // start logging to OS clipboard IMGUI_FUNCTION(LogToClipboard) -OPTIONAL_INT_ARG(max_depth, -1) -CALL_FUNCTION_NO_RET(LogToClipboard, max_depth) +OPTIONAL_INT_ARG(auto_open_depth, -1) +CALL_FUNCTION_NO_RET(LogToClipboard, auto_open_depth) END_IMGUI_FUNC // IMGUI_API void LogFinish(); // stop logging (close file, etc.) IMGUI_FUNCTION(LogFinish) @@ -1233,29 +1304,31 @@ END_IMGUI_FUNC // Unsupported arg type ...) IM_FMTARGS(1 // IMGUI_API bool BeginDragDropSource(ImGuiDragDropFlags flags = 0); // call when the current item is active. If this return true, you can call SetDragDropPayload() + EndDragDropSource() // Unsupported arg type ImGuiDragDropFlags flags = 0 -// IMGUI_API bool SetDragDropPayload(const char* type, const void* data, size_t size, ImGuiCond cond = 0);// type is a user defined string of maximum 32 characters. Strings starting with '_' are reserved for dear imgui internal types. Data is copied and held by imgui. +// IMGUI_API bool SetDragDropPayload(const char* type, const void* data, size_t sz, ImGuiCond cond = 0); // type is a user defined string of maximum 32 characters. Strings starting with '_' are reserved for dear imgui internal types. Data is copied and held by imgui. // Unsupported arg type const void* data -// Unsupported arg type size_t size +// Unsupported arg type size_t sz // Unsupported arg type ImGuiCond cond = 0 // IMGUI_API void EndDragDropSource(); // only call EndDragDropSource() if BeginDragDropSource() returns true! IMGUI_FUNCTION(EndDragDropSource) CALL_FUNCTION_NO_RET(EndDragDropSource) -POP_END_STACK(12) +POP_END_STACK(14) END_IMGUI_FUNC -// IMGUI_API bool BeginDragDropTarget(); // call after submitting an item that may receive an item. If this returns true, you can call AcceptDragDropPayload() + EndDragDropTarget() +// IMGUI_API bool BeginDragDropTarget(); // call after submitting an item that may receive a payload. If this returns true, you can call AcceptDragDropPayload() + EndDragDropTarget() IMGUI_FUNCTION(BeginDragDropTarget) CALL_FUNCTION(BeginDragDropTarget, bool) -IF_RET_ADD_END_STACK(13) +IF_RET_ADD_END_STACK(15) PUSH_BOOL(ret) END_IMGUI_FUNC -// IMGUI_API const ImGuiPayload* AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags = 0); // accept contents of a given type. If ImGuiDragDropFlags_AcceptBeforeDelivery is set you can peek into the payload before the mouse button is released. +// IMGUI_API const ImGuiPayload* AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags = 0); // accept contents of a given type. If ImGuiDragDropFlags_AcceptBeforeDelivery is set you can peek into the payload before the mouse button is released. // Unsupported return type const // Unsupported arg type ImGuiDragDropFlags flags = 0 -// IMGUI_API void EndDragDropTarget(); // only call EndDragDropTarget() if BeginDragDropTarget() returns true! +// IMGUI_API void EndDragDropTarget(); // only call EndDragDropTarget() if BeginDragDropTarget() returns true! IMGUI_FUNCTION(EndDragDropTarget) CALL_FUNCTION_NO_RET(EndDragDropTarget) -POP_END_STACK(13) +POP_END_STACK(15) END_IMGUI_FUNC +// IMGUI_API const ImGuiPayload* GetDragDropPayload(); // peek directly into the current payload from anywhere. may return NULL. use ImGuiPayload::IsDataType() to test for the payload type. +// Unsupported return type const // IMGUI_API void PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect); IMGUI_FUNCTION(PushClipRect) IM_VEC_2_ARG(clip_rect_min) @@ -1267,7 +1340,7 @@ END_IMGUI_FUNC IMGUI_FUNCTION(PopClipRect) CALL_FUNCTION_NO_RET(PopClipRect) END_IMGUI_FUNC -// IMGUI_API void SetItemDefaultFocus(); // make last item the default focused item of a window. Please use instead of "if (IsWindowAppearing()) SetScrollHere()" to signify "default item". +// IMGUI_API void SetItemDefaultFocus(); // make last item the default focused item of a window. IMGUI_FUNCTION(SetItemDefaultFocus) CALL_FUNCTION_NO_RET(SetItemDefaultFocus) END_IMGUI_FUNC @@ -1278,7 +1351,7 @@ CALL_FUNCTION_NO_RET(SetKeyboardFocusHere, offset) END_IMGUI_FUNC // IMGUI_API bool IsItemHovered(ImGuiHoveredFlags flags = 0); // is the last item hovered? (and usable, aka not blocked by a popup, etc.). See ImGuiHoveredFlags for more options. // Unsupported arg type ImGuiHoveredFlags flags = 0 -// IMGUI_API bool IsItemActive(); // is the last item active? (e.g. button being held, text field being edited- items that don't interact will always return false) +// IMGUI_API bool IsItemActive(); // is the last item active? (e.g. button being held, text field being edited. This will continuously return true while holding mouse button on an item. Items that don't interact will always return false) IMGUI_FUNCTION(IsItemActive) CALL_FUNCTION(IsItemActive, bool) PUSH_BOOL(ret) @@ -1288,45 +1361,65 @@ IMGUI_FUNCTION(IsItemFocused) CALL_FUNCTION(IsItemFocused, bool) PUSH_BOOL(ret) END_IMGUI_FUNC -// IMGUI_API bool IsItemClicked(int mouse_button = 0); // is the last item clicked? (e.g. button/node just clicked on) +// IMGUI_API bool IsItemClicked(int mouse_button = 0); // is the last item clicked? (e.g. button/node just clicked on) == IsMouseClicked(mouse_button) && IsItemHovered() IMGUI_FUNCTION(IsItemClicked) OPTIONAL_INT_ARG(mouse_button, 0) CALL_FUNCTION(IsItemClicked, bool, mouse_button) PUSH_BOOL(ret) END_IMGUI_FUNC -// IMGUI_API bool IsItemVisible(); // is the last item visible? (aka not out of sight due to clipping/scrolling.) +// IMGUI_API bool IsItemVisible(); // is the last item visible? (items may be out of sight because of clipping/scrolling) IMGUI_FUNCTION(IsItemVisible) CALL_FUNCTION(IsItemVisible, bool) PUSH_BOOL(ret) END_IMGUI_FUNC -// IMGUI_API bool IsAnyItemHovered(); +// IMGUI_API bool IsItemEdited(); // did the last item modify its underlying value this frame? or was pressed? This is generally the same as the "bool" return value of many widgets. +IMGUI_FUNCTION(IsItemEdited) +CALL_FUNCTION(IsItemEdited, bool) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API bool IsItemActivated(); // was the last item just made active (item was previously inactive). +IMGUI_FUNCTION(IsItemActivated) +CALL_FUNCTION(IsItemActivated, bool) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API bool IsItemDeactivated(); // was the last item just made inactive (item was previously active). Useful for Undo/Redo patterns with widgets that requires continuous editing. +IMGUI_FUNCTION(IsItemDeactivated) +CALL_FUNCTION(IsItemDeactivated, bool) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API bool IsItemDeactivatedAfterEdit(); // was the last item just made inactive and made a value change when it was active? (e.g. Slider/Drag moved). Useful for Undo/Redo patterns with widgets that requires continuous editing. Note that you may get false positives (some widgets such as Combo()/ListBox()/Selectable() will return true even when clicking an already selected item). +IMGUI_FUNCTION(IsItemDeactivatedAfterEdit) +CALL_FUNCTION(IsItemDeactivatedAfterEdit, bool) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API bool IsAnyItemHovered(); // is any item hovered? IMGUI_FUNCTION(IsAnyItemHovered) CALL_FUNCTION(IsAnyItemHovered, bool) PUSH_BOOL(ret) END_IMGUI_FUNC -// IMGUI_API bool IsAnyItemActive(); +// IMGUI_API bool IsAnyItemActive(); // is any item active? IMGUI_FUNCTION(IsAnyItemActive) CALL_FUNCTION(IsAnyItemActive, bool) PUSH_BOOL(ret) END_IMGUI_FUNC -// IMGUI_API bool IsAnyItemFocused(); +// IMGUI_API bool IsAnyItemFocused(); // is any item focused? IMGUI_FUNCTION(IsAnyItemFocused) CALL_FUNCTION(IsAnyItemFocused, bool) PUSH_BOOL(ret) END_IMGUI_FUNC -// IMGUI_API ImVec2 GetItemRectMin(); // get bounding rectangle of last item, in screen space +// IMGUI_API ImVec2 GetItemRectMin(); // get upper-left bounding rectangle of the last item (screen space) IMGUI_FUNCTION(GetItemRectMin) CALL_FUNCTION(GetItemRectMin, ImVec2) PUSH_NUMBER(ret.x) PUSH_NUMBER(ret.y) END_IMGUI_FUNC -// IMGUI_API ImVec2 GetItemRectMax(); // " +// IMGUI_API ImVec2 GetItemRectMax(); // get lower-right bounding rectangle of the last item (screen space) IMGUI_FUNCTION(GetItemRectMax) CALL_FUNCTION(GetItemRectMax, ImVec2) PUSH_NUMBER(ret.x) PUSH_NUMBER(ret.y) END_IMGUI_FUNC -// IMGUI_API ImVec2 GetItemRectSize(); // get size of last item, in screen space +// IMGUI_API ImVec2 GetItemRectSize(); // get size of last item IMGUI_FUNCTION(GetItemRectSize) CALL_FUNCTION(GetItemRectSize, ImVec2) PUSH_NUMBER(ret.x) @@ -1349,18 +1442,17 @@ IM_VEC_2_ARG(rect_max) CALL_FUNCTION(IsRectVisible, bool, rect_min, rect_max) PUSH_BOOL(ret) END_IMGUI_FUNC -// IMGUI_API float GetTime(); -IMGUI_FUNCTION(GetTime) -CALL_FUNCTION(GetTime, float) -PUSH_NUMBER(ret) -END_IMGUI_FUNC -// IMGUI_API int GetFrameCount(); +// IMGUI_API double GetTime(); // get global imgui time. incremented by io.DeltaTime every frame. +// Unsupported return type double +// IMGUI_API int GetFrameCount(); // get global imgui frame count. incremented by 1 every frame. // Unsupported return type int -// IMGUI_API ImDrawList* GetOverlayDrawList(); // this draw list will be the last rendered one, useful to quickly draw overlays shapes/text +// IMGUI_API ImDrawList* GetBackgroundDrawList(); // this draw list will be the first rendering one. Useful to quickly draw shapes/text behind dear imgui contents. +// Unsupported return type ImDrawList* +// IMGUI_API ImDrawList* GetForegroundDrawList(); // this draw list will be the last rendered one. Useful to quickly draw shapes/text over dear imgui contents. // Unsupported return type ImDrawList* -// IMGUI_API ImDrawListSharedData* GetDrawListSharedData(); // you may use this when creating your own ImDrawList instances +// IMGUI_API ImDrawListSharedData* GetDrawListSharedData(); // you may use this when creating your own ImDrawList instances. // Unsupported return type ImDrawListSharedData* -// IMGUI_API const char* GetStyleColorName(ImGuiCol idx); +// IMGUI_API const char* GetStyleColorName(ImGuiCol idx); // get a string corresponding to the enum value (for display, saving, etc.). // Unsupported return type const // IMGUI_API void SetStateStorage(ImGuiStorage* storage); // replace current window storage with our own (if you want to manipulate it yourself, typically clear subsection of it) // Unsupported arg type ImGuiStorage* storage @@ -1392,13 +1484,13 @@ UINT_ARG(id) IM_VEC_2_ARG(size) OPTIONAL_INT_ARG(flags, 0) CALL_FUNCTION(BeginChildFrame, bool, id, size, flags) -IF_RET_ADD_END_STACK(14) +IF_RET_ADD_END_STACK(16) PUSH_BOOL(ret) END_IMGUI_FUNC // IMGUI_API void EndChildFrame(); // always call EndChildFrame() regardless of BeginChildFrame() return values (which indicates a collapsed/clipped window) IMGUI_FUNCTION(EndChildFrame) CALL_FUNCTION_NO_RET(EndChildFrame) -POP_END_STACK(14) +POP_END_STACK(16) END_IMGUI_FUNC // IMGUI_API ImVec4 ColorConvertU32ToFloat4(ImU32 in); // Unsupported return type ImVec4 @@ -1418,7 +1510,7 @@ END_IMGUI_FUNC // Unsupported arg type float& out_b // IMGUI_API int GetKeyIndex(ImGuiKey imgui_key); // map ImGuiKey_* values into user's key index. == io.KeyMap[key] // Unsupported return type int -// IMGUI_API bool IsKeyDown(int user_key_index); // is key being held. == io.KeysDown[user_key_index]. note that imgui doesn't know the semantic of each entry of io.KeyDown[]. Use your own indices/enums according to how your backend/engine stored them into KeyDown[]! +// IMGUI_API bool IsKeyDown(int user_key_index); // is key being held. == io.KeysDown[user_key_index]. note that imgui doesn't know the semantic of each entry of io.KeysDown[]. Use your own indices/enums according to how your backend/engine stored them into io.KeysDown[]! IMGUI_FUNCTION(IsKeyDown) INT_ARG(user_key_index) CALL_FUNCTION(IsKeyDown, bool, user_key_index) @@ -1439,7 +1531,7 @@ PUSH_BOOL(ret) END_IMGUI_FUNC // IMGUI_API int GetKeyPressedAmount(int key_index, float repeat_delay, float rate); // uses provided repeat rate/delay. return a count, most often 0 or 1 but might be >1 if RepeatRate is small enough that DeltaTime > RepeatRate // Unsupported return type int -// IMGUI_API bool IsMouseDown(int button); // is mouse button held +// IMGUI_API bool IsMouseDown(int button); // is mouse button held (0=left, 1=right, 2=middle) IMGUI_FUNCTION(IsMouseDown) INT_ARG(button) CALL_FUNCTION(IsMouseDown, bool, button) @@ -1450,7 +1542,7 @@ IMGUI_FUNCTION(IsAnyMouseDown) CALL_FUNCTION(IsAnyMouseDown, bool) PUSH_BOOL(ret) END_IMGUI_FUNC -// IMGUI_API bool IsMouseClicked(int button, bool repeat = false); // did mouse button clicked (went from !Down to Down) +// IMGUI_API bool IsMouseClicked(int button, bool repeat = false); // did mouse button clicked (went from !Down to Down) (0=left, 1=right, 2=middle) IMGUI_FUNCTION(IsMouseClicked) INT_ARG(button) OPTIONAL_BOOL_ARG(repeat, false) @@ -1476,7 +1568,7 @@ OPTIONAL_NUMBER_ARG(lock_threshold, -1.0f) CALL_FUNCTION(IsMouseDragging, bool, button, lock_threshold) PUSH_BOOL(ret) END_IMGUI_FUNC -// IMGUI_API bool IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip = true); // is mouse hovering given bounding rect (in screen space). clipped by current clipping settings. disregarding of consideration of focus/window ordering/blocked by a popup. +// IMGUI_API bool IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip = true); // is mouse hovering given bounding rect (in screen space). clipped by current clipping settings, but disregarding of other consideration of focus/window ordering/popup-block. IMGUI_FUNCTION(IsMouseHoveringRect) IM_VEC_2_ARG(r_min) IM_VEC_2_ARG(r_max) @@ -1484,7 +1576,7 @@ OPTIONAL_BOOL_ARG(clip, true) CALL_FUNCTION(IsMouseHoveringRect, bool, r_min, r_max, clip) PUSH_BOOL(ret) END_IMGUI_FUNC -// IMGUI_API bool IsMousePosValid(const ImVec2* mouse_pos = NULL); // +// IMGUI_API bool IsMousePosValid(const ImVec2* mouse_pos = NULL); // by convention we use (-FLT_MAX,-FLT_MAX) to denote that there is no mouse // Unsupported arg type const ImVec2* mouse_pos = NULL // IMGUI_API ImVec2 GetMousePos(); // shortcut to ImGui::GetIO().MousePos provided by user, to be consistent with other calls IMGUI_FUNCTION(GetMousePos) @@ -1498,7 +1590,7 @@ CALL_FUNCTION(GetMousePosOnOpeningCurrentPopup, ImVec2) PUSH_NUMBER(ret.x) PUSH_NUMBER(ret.y) END_IMGUI_FUNC -// IMGUI_API ImVec2 GetMouseDragDelta(int button = 0, float lock_threshold = -1.0f); // dragging amount since clicking. if lock_threshold < -1.0f uses io.MouseDraggingThreshold +// IMGUI_API ImVec2 GetMouseDragDelta(int button = 0, float lock_threshold = -1.0f); // return the delta from the initial clicking position. This is locked and return 0.0f until the mouse moves past a distance threshold at least once. If lock_threshold < -1.0f uses io.MouseDraggingThreshold IMGUI_FUNCTION(GetMouseDragDelta) OPTIONAL_INT_ARG(button, 0) OPTIONAL_NUMBER_ARG(lock_threshold, -1.0f) @@ -1518,15 +1610,15 @@ IMGUI_FUNCTION(SetMouseCursor) INT_ARG(type) CALL_FUNCTION_NO_RET(SetMouseCursor, type) END_IMGUI_FUNC -// IMGUI_API void CaptureKeyboardFromApp(bool capture = true); // manually override io.WantCaptureKeyboard flag next frame (said flag is entirely left for your application to handle). e.g. force capture keyboard when your widget is being hovered. +// IMGUI_API void CaptureKeyboardFromApp(bool want_capture_keyboard_value = true); // attention: misleading name! manually override io.WantCaptureKeyboard flag next frame (said flag is entirely left for your application to handle). e.g. force capture keyboard when your widget is being hovered. This is equivalent to setting "io.WantCaptureKeyboard = want_capture_keyboard_value"; after the next NewFrame() call. IMGUI_FUNCTION(CaptureKeyboardFromApp) -OPTIONAL_BOOL_ARG(capture, true) -CALL_FUNCTION_NO_RET(CaptureKeyboardFromApp, capture) +OPTIONAL_BOOL_ARG(want_capture_keyboard_value, true) +CALL_FUNCTION_NO_RET(CaptureKeyboardFromApp, want_capture_keyboard_value) END_IMGUI_FUNC -// IMGUI_API void CaptureMouseFromApp(bool capture = true); // manually override io.WantCaptureMouse flag next frame (said flag is entirely left for your application to handle). +// IMGUI_API void CaptureMouseFromApp(bool want_capture_mouse_value = true); // attention: misleading name! manually override io.WantCaptureMouse flag next frame (said flag is entirely left for your application to handle). This is equivalent to setting "io.WantCaptureMouse = want_capture_mouse_value;" after the next NewFrame() call. IMGUI_FUNCTION(CaptureMouseFromApp) -OPTIONAL_BOOL_ARG(capture, true) -CALL_FUNCTION_NO_RET(CaptureMouseFromApp, capture) +OPTIONAL_BOOL_ARG(want_capture_mouse_value, true) +CALL_FUNCTION_NO_RET(CaptureMouseFromApp, want_capture_mouse_value) END_IMGUI_FUNC // IMGUI_API const char* GetClipboardText(); // Unsupported return type const @@ -1535,10 +1627,25 @@ IMGUI_FUNCTION(SetClipboardText) LABEL_ARG(text) CALL_FUNCTION_NO_RET(SetClipboardText, text) END_IMGUI_FUNC -// IMGUI_API void SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void(*free_func)(void* ptr, void* user_data), void* user_data = NULL); +// IMGUI_API void LoadIniSettingsFromDisk(const char* ini_filename); // call after CreateContext() and before the first call to NewFrame(). NewFrame() automatically calls LoadIniSettingsFromDisk(io.IniFilename). +IMGUI_FUNCTION(LoadIniSettingsFromDisk) +LABEL_ARG(ini_filename) +CALL_FUNCTION_NO_RET(LoadIniSettingsFromDisk, ini_filename) +END_IMGUI_FUNC +// IMGUI_API void LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size=0); // call after CreateContext() and before the first call to NewFrame() to provide .ini data from your own data source. +// Unsupported arg type size_t ini_size=0 +// IMGUI_API void SaveIniSettingsToDisk(const char* ini_filename); // this is automatically called (if io.IniFilename is not empty) a few seconds after any modification that should be reflected in the .ini file (and also by DestroyContext). +IMGUI_FUNCTION(SaveIniSettingsToDisk) +LABEL_ARG(ini_filename) +CALL_FUNCTION_NO_RET(SaveIniSettingsToDisk, ini_filename) +END_IMGUI_FUNC +// IMGUI_API const char* SaveIniSettingsToMemory(size_t* out_ini_size = NULL); // return a zero-terminated string with the .ini data which you can save by your own mean. call when io.WantSaveIniSettings is set, then save data by your own mean and clear io.WantSaveIniSettings. +// Unsupported return type const +// Unsupported arg type size_t* out_ini_size = NULL +// IMGUI_API void SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data = NULL); // Unsupported arg type void* (*alloc_func)(size_t sz // Unsupported arg type void* user_data) -// Unsupported arg type void(*free_func)(void* ptr +// Unsupported arg type void (*free_func)(void* ptr // Unsupported arg type void* user_data) // Unsupported arg type void* user_data = NULL // IMGUI_API void* MemAlloc(size_t size); @@ -1554,14 +1661,16 @@ END_STACK_OPTION(3, PopStyleVar) END_STACK_OPTION(4, EndGroup) END_STACK_OPTION(5, EndCombo) END_STACK_OPTION(6, TreePop) -END_STACK_OPTION(7, EndTooltip) -END_STACK_OPTION(8, EndMainMenuBar) -END_STACK_OPTION(9, EndMenuBar) -END_STACK_OPTION(10, EndMenu) +END_STACK_OPTION(7, EndMainMenuBar) +END_STACK_OPTION(8, EndMenuBar) +END_STACK_OPTION(9, EndMenu) +END_STACK_OPTION(10, EndTooltip) END_STACK_OPTION(11, EndPopup) -END_STACK_OPTION(12, EndDragDropSource) -END_STACK_OPTION(13, EndDragDropTarget) -END_STACK_OPTION(14, EndChildFrame) +END_STACK_OPTION(12, EndTabBar) +END_STACK_OPTION(13, EndTabItem) +END_STACK_OPTION(14, EndDragDropSource) +END_STACK_OPTION(15, EndDragDropTarget) +END_STACK_OPTION(16, EndChildFrame) END_STACK_END //enum ImGuiWindowFlags_ @@ -1573,12 +1682,18 @@ END_STACK_END //enum ImGuiComboFlags_ +//enum ImGuiTabBarFlags_ + +//enum ImGuiTabItemFlags_ + //enum ImGuiFocusedFlags_ //enum ImGuiHoveredFlags_ //enum ImGuiDragDropFlags_ +//enum ImGuiDataType_ + //enum ImGuiDir_ //enum ImGuiKey_ @@ -1599,16 +1714,22 @@ END_STACK_END //enum ImGuiCond_ +//struct ImVector + //struct ImGuiStyle //struct ImGuiIO +//struct ImGuiInputTextCallbackData + +//struct ImGuiSizeCallbackData + +//struct ImGuiPayload + //namespace ImGui END_STACK_START END_STACK_END -//class ImVector - //struct ImGuiOnceUponAFrame //struct ImGuiTextFilter @@ -1617,16 +1738,10 @@ END_STACK_END //struct ImGuiStorage -//struct ImGuiTextEditCallbackData - -//struct ImGuiSizeCallbackData - -//struct ImGuiPayload +//struct ImGuiListClipper //struct ImColor -//struct ImGuiListClipper - //struct ImDrawCmd //struct ImDrawVert @@ -1639,19 +1754,19 @@ END_STACK_END //struct ImDrawList -// IMGUI_API void DeIndexAllBuffers(); // Helper to convert all buffers from indexed to non-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering! -/* -IMGUI_FUNCTION_DRAW_LIST(DeIndexAllBuffers) -DRAW_LIST_CALL_FUNCTION_NO_RET(DeIndexAllBuffers) -END_IMGUI_FUNC +// [Bruno Levy] commented-out manually these two ones (they don't compile) -// IMGUI_API void ScaleClipRects(const ImVec2& sc); // Helper to scale the ClipRect field of each ImDrawCmd. Use if your final output buffer is at a different scale than ImGui expects, or if there is a difference between your window resolution and framebuffer resolution. -IMGUI_FUNCTION_DRAW_LIST(ScaleClipRects) -IM_VEC_2_ARG(sc) -DRAW_LIST_CALL_FUNCTION_NO_RET(ScaleClipRects, sc) -END_IMGUI_FUNC -*/ +// IMGUI_API void DeIndexAllBuffers(); // Helper to convert all buffers from indexed to non-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering! +//IMGUI_FUNCTION_DRAW_LIST(DeIndexAllBuffers) +//DRAW_LIST_CALL_FUNCTION_NO_RET(DeIndexAllBuffers) +//END_IMGUI_FUNC + +// IMGUI_API void ScaleClipRects(const ImVec2& fb_scale); // Helper to scale the ClipRect field of each ImDrawCmd. Use if your final output buffer is at a different scale than ImGui expects, or if there is a difference between your window resolution and framebuffer resolution. +//IMGUI_FUNCTION_DRAW_LIST(ScaleClipRects) +//IM_VEC_2_ARG(fb_scale) +//DRAW_LIST_CALL_FUNCTION_NO_RET(ScaleClipRects, fb_scale) +//END_IMGUI_FUNC //struct ImDrawData @@ -1659,6 +1774,8 @@ END_IMGUI_FUNC //struct ImFontGlyph +//struct ImFontGlyphRangesBuilder + //enum ImFontAtlasFlags_ //struct ImFontAtlas diff --git a/src/lib/geogram_gfx/third_party/imgui_lua_bindings/imgui_iterator.h.000 b/src/lib/geogram_gfx/third_party/imgui_lua_bindings/imgui_iterator.h.000 new file mode 100644 index 00000000..a3d47a75 --- /dev/null +++ b/src/lib/geogram_gfx/third_party/imgui_lua_bindings/imgui_iterator.h.000 @@ -0,0 +1,1665 @@ + +//struct ImVec2 + +//struct ImVec4 + +//namespace ImGui + +// IMGUI_API ImGuiContext* CreateContext(ImFontAtlas* shared_font_atlas = NULL); +// Unsupported return type ImGuiContext* +// Unsupported arg type ImFontAtlas* shared_font_atlas = NULL +// IMGUI_API void DestroyContext(ImGuiContext* ctx = NULL); // NULL = destroy current context +// Unsupported arg type ImGuiContext* ctx = NULL +// IMGUI_API ImGuiContext* GetCurrentContext(); +// Unsupported return type ImGuiContext* +// IMGUI_API void SetCurrentContext(ImGuiContext* ctx); +// Unsupported arg type ImGuiContext* ctx +// IMGUI_API ImGuiIO& GetIO(); +// Unsupported return type ImGuiIO& +// IMGUI_API ImGuiStyle& GetStyle(); +// Unsupported return type ImGuiStyle& +// IMGUI_API void NewFrame(); // start a new ImGui frame, you can submit any command from this point until Render()/EndFrame(). +//Not allowed to use this function +// IMGUI_API void Render(); // ends the ImGui frame, finalize the draw data. (Obsolete: optionally call io.RenderDrawListsFn if set. Nowadays, prefer calling your render function yourself.) +//Not allowed to use this function +// IMGUI_API ImDrawData* GetDrawData(); // valid after Render() and until the next call to NewFrame(). this is what you have to render. (Obsolete: this used to be passed to your io.RenderDrawListsFn() function.) +// Unsupported return type ImDrawData* +// IMGUI_API void EndFrame(); // ends the ImGui frame. automatically called by Render(), so most likely don't need to ever call that yourself directly. If you don't need to render you may call EndFrame() but you'll have wasted CPU already. If you don't need to render, better to not create any imgui windows instead! +IMGUI_FUNCTION(EndFrame) +CALL_FUNCTION_NO_RET(EndFrame) +POP_END_STACK(0) +END_IMGUI_FUNC +// IMGUI_API void ShowDemoWindow(bool* p_open = NULL); // create demo/test window (previously called ShowTestWindow). demonstrate most ImGui features. call this to learn about the library! try to make it always available in your application! +IMGUI_FUNCTION(ShowDemoWindow) +OPTIONAL_BOOL_POINTER_ARG(p_open) +CALL_FUNCTION_NO_RET(ShowDemoWindow, p_open) +END_BOOL_POINTER(p_open) +END_IMGUI_FUNC +// IMGUI_API void ShowMetricsWindow(bool* p_open = NULL); // create metrics window. display ImGui internals: draw commands (with individual draw calls and vertices), window list, basic internal state, etc. +IMGUI_FUNCTION(ShowMetricsWindow) +OPTIONAL_BOOL_POINTER_ARG(p_open) +CALL_FUNCTION_NO_RET(ShowMetricsWindow, p_open) +END_BOOL_POINTER(p_open) +END_IMGUI_FUNC +// IMGUI_API void ShowStyleEditor(ImGuiStyle* ref = NULL); // add style editor block (not a window). you can pass in a reference ImGuiStyle structure to compare to, revert to and save to (else it uses the default style) +// Unsupported arg type ImGuiStyle* ref = NULL +// IMGUI_API bool ShowStyleSelector(const char* label); // add style selector block (not a window), essentially a combo listing the default styles. +IMGUI_FUNCTION(ShowStyleSelector) +LABEL_ARG(label) +CALL_FUNCTION(ShowStyleSelector, bool, label) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API void ShowFontSelector(const char* label); // add font selector block (not a window), essentially a combo listing the loaded fonts. +IMGUI_FUNCTION(ShowFontSelector) +LABEL_ARG(label) +CALL_FUNCTION_NO_RET(ShowFontSelector, label) +END_IMGUI_FUNC +// IMGUI_API void ShowUserGuide(); // add basic help/info block (not a window): how to manipulate ImGui as a end-user (mouse/keyboard controls). +IMGUI_FUNCTION(ShowUserGuide) +CALL_FUNCTION_NO_RET(ShowUserGuide) +END_IMGUI_FUNC +// IMGUI_API const char* GetVersion(); // get a version string e.g. "1.23" +// Unsupported return type const +// IMGUI_API void StyleColorsDark(ImGuiStyle* dst = NULL); // new, recommended style (default) +// Unsupported arg type ImGuiStyle* dst = NULL +// IMGUI_API void StyleColorsClassic(ImGuiStyle* dst = NULL); // classic imgui style +// Unsupported arg type ImGuiStyle* dst = NULL +// IMGUI_API void StyleColorsLight(ImGuiStyle* dst = NULL); // best used with borders and a custom, thicker font +// Unsupported arg type ImGuiStyle* dst = NULL +// IMGUI_API bool Begin(const char* name, bool* p_open = NULL, ImGuiWindowFlags flags = 0); +IMGUI_FUNCTION(Begin) +LABEL_ARG(name) +OPTIONAL_BOOL_POINTER_ARG(p_open) +OPTIONAL_INT_ARG(flags, 0) +CALL_FUNCTION(Begin, bool, name, p_open, flags) +IF_RET_ADD_END_STACK(1) +PUSH_BOOL(ret) +END_BOOL_POINTER(p_open) +END_IMGUI_FUNC +// IMGUI_API void End(); +IMGUI_FUNCTION(End) +CALL_FUNCTION_NO_RET(End) +POP_END_STACK(1) +END_IMGUI_FUNC +// IMGUI_API bool BeginChild(const char* str_id, const ImVec2& size = ImVec2 0 0, bool border = false, ImGuiWindowFlags flags = 0); // Begin a scrolling region. size==0.0f: use remaining window size, size<0.0f: use remaining window size minus abs(size). size>0.0f: fixed size. each axis can use a different mode, e.g. ImVec2 0 400. +IMGUI_FUNCTION(BeginChild) +LABEL_ARG(str_id) +OPTIONAL_IM_VEC_2_ARG(size, 0, 0) +OPTIONAL_BOOL_ARG(border, false) +OPTIONAL_INT_ARG(flags, 0) +CALL_FUNCTION(BeginChild, bool, str_id, size, border, flags) +IF_RET_ADD_END_STACK(2) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API bool BeginChild(ImGuiID id, const ImVec2& size = ImVec2 0 0, bool border = false, ImGuiWindowFlags flags = 0); +IMGUI_FUNCTION(BeginChild_4) +UINT_ARG(id) +OPTIONAL_IM_VEC_2_ARG(size, 0, 0) +OPTIONAL_BOOL_ARG(border, false) +OPTIONAL_INT_ARG(flags, 0) +CALL_FUNCTION(BeginChild, bool, id, size, border, flags) +IF_RET_ADD_END_STACK(2) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API void EndChild(); +IMGUI_FUNCTION(EndChild) +CALL_FUNCTION_NO_RET(EndChild) +POP_END_STACK(2) +END_IMGUI_FUNC +// IMGUI_API bool IsWindowAppearing(); +IMGUI_FUNCTION(IsWindowAppearing) +CALL_FUNCTION(IsWindowAppearing, bool) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API bool IsWindowCollapsed(); +IMGUI_FUNCTION(IsWindowCollapsed) +CALL_FUNCTION(IsWindowCollapsed, bool) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API bool IsWindowFocused(ImGuiFocusedFlags flags=0); // is current window focused? or its root/child, depending on flags. see flags for options. +// Unsupported arg type ImGuiFocusedFlags flags=0 +// IMGUI_API bool IsWindowHovered(ImGuiHoveredFlags flags=0); // is current window hovered (and typically: not blocked by a popup/modal)? see flags for options. NB: If you are trying to check whether your mouse should be dispatched to imgui or to your app, you should use the 'io.WantCaptureMouse' boolean for that! Please read the FAQ! +// Unsupported arg type ImGuiHoveredFlags flags=0 +// IMGUI_API ImDrawList* GetWindowDrawList(); // get draw list associated to the window, to append your own drawing primitives +// Unsupported return type ImDrawList* +// IMGUI_API ImVec2 GetWindowPos(); // get current window position in screen space (useful if you want to do your own drawing via the DrawList API) +IMGUI_FUNCTION(GetWindowPos) +CALL_FUNCTION(GetWindowPos, ImVec2) +PUSH_NUMBER(ret.x) +PUSH_NUMBER(ret.y) +END_IMGUI_FUNC +// IMGUI_API ImVec2 GetWindowSize(); // get current window size +IMGUI_FUNCTION(GetWindowSize) +CALL_FUNCTION(GetWindowSize, ImVec2) +PUSH_NUMBER(ret.x) +PUSH_NUMBER(ret.y) +END_IMGUI_FUNC +// IMGUI_API float GetWindowWidth(); // get current window width (shortcut for GetWindowSize().x) +IMGUI_FUNCTION(GetWindowWidth) +CALL_FUNCTION(GetWindowWidth, float) +PUSH_NUMBER(ret) +END_IMGUI_FUNC +// IMGUI_API float GetWindowHeight(); // get current window height (shortcut for GetWindowSize().y) +IMGUI_FUNCTION(GetWindowHeight) +CALL_FUNCTION(GetWindowHeight, float) +PUSH_NUMBER(ret) +END_IMGUI_FUNC +// IMGUI_API ImVec2 GetContentRegionMax(); // current content boundaries (typically window boundaries including scrolling, or current column boundaries), in windows coordinates +IMGUI_FUNCTION(GetContentRegionMax) +CALL_FUNCTION(GetContentRegionMax, ImVec2) +PUSH_NUMBER(ret.x) +PUSH_NUMBER(ret.y) +END_IMGUI_FUNC +// IMGUI_API ImVec2 GetContentRegionAvail(); // == GetContentRegionMax() - GetCursorPos() +IMGUI_FUNCTION(GetContentRegionAvail) +CALL_FUNCTION(GetContentRegionAvail, ImVec2) +PUSH_NUMBER(ret.x) +PUSH_NUMBER(ret.y) +END_IMGUI_FUNC +// IMGUI_API float GetContentRegionAvailWidth(); // +IMGUI_FUNCTION(GetContentRegionAvailWidth) +CALL_FUNCTION(GetContentRegionAvailWidth, float) +PUSH_NUMBER(ret) +END_IMGUI_FUNC +// IMGUI_API ImVec2 GetWindowContentRegionMin(); // content boundaries min (roughly (0,0)-Scroll), in window coordinates +IMGUI_FUNCTION(GetWindowContentRegionMin) +CALL_FUNCTION(GetWindowContentRegionMin, ImVec2) +PUSH_NUMBER(ret.x) +PUSH_NUMBER(ret.y) +END_IMGUI_FUNC +// IMGUI_API ImVec2 GetWindowContentRegionMax(); // content boundaries max (roughly (0,0)+Size-Scroll) where Size can be override with SetNextWindowContentSize(), in window coordinates +IMGUI_FUNCTION(GetWindowContentRegionMax) +CALL_FUNCTION(GetWindowContentRegionMax, ImVec2) +PUSH_NUMBER(ret.x) +PUSH_NUMBER(ret.y) +END_IMGUI_FUNC +// IMGUI_API float GetWindowContentRegionWidth(); // +IMGUI_FUNCTION(GetWindowContentRegionWidth) +CALL_FUNCTION(GetWindowContentRegionWidth, float) +PUSH_NUMBER(ret) +END_IMGUI_FUNC +// IMGUI_API void SetNextWindowPos(const ImVec2& pos, ImGuiCond cond = 0, const ImVec2& pivot = ImVec2 0 0); // set next window position. call before Begin(). use pivot=(0.5f,0.5f) to center on given point, etc. +// Unsupported arg type ImGuiCond cond = 0 +// IMGUI_API void SetNextWindowSize(const ImVec2& size, ImGuiCond cond = 0); // set next window size. set axis to 0.0f to force an auto-fit on this axis. call before Begin() +// Unsupported arg type ImGuiCond cond = 0 +// IMGUI_API void SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback = NULL, void* custom_callback_data = NULL); // set next window size limits. use -1,-1 on either X/Y axis to preserve the current size. Use callback to apply non-trivial programmatic constraints. +// Unsupported arg type ImGuiSizeCallback custom_callback = NULL +// Unsupported arg type void* custom_callback_data = NULL +// IMGUI_API void SetNextWindowContentSize(const ImVec2& size); // set next window content size (~ enforce the range of scrollbars). not including window decorations (title bar, menu bar, etc.). set an axis to 0.0f to leave it automatic. call before Begin() +IMGUI_FUNCTION(SetNextWindowContentSize) +IM_VEC_2_ARG(size) +CALL_FUNCTION_NO_RET(SetNextWindowContentSize, size) +END_IMGUI_FUNC +// IMGUI_API void SetNextWindowCollapsed(bool collapsed, ImGuiCond cond = 0); // set next window collapsed state. call before Begin() +// Unsupported arg type ImGuiCond cond = 0 +// IMGUI_API void SetNextWindowFocus(); // set next window to be focused / front-most. call before Begin() +IMGUI_FUNCTION(SetNextWindowFocus) +CALL_FUNCTION_NO_RET(SetNextWindowFocus) +END_IMGUI_FUNC +// IMGUI_API void SetNextWindowBgAlpha(float alpha); // set next window background color alpha. helper to easily modify ImGuiCol_WindowBg/ChildBg/PopupBg. +IMGUI_FUNCTION(SetNextWindowBgAlpha) +NUMBER_ARG(alpha) +CALL_FUNCTION_NO_RET(SetNextWindowBgAlpha, alpha) +END_IMGUI_FUNC +// IMGUI_API void SetWindowPos(const ImVec2& pos, ImGuiCond cond = 0); // (not recommended) set current window position - call within Begin()/End(). prefer using SetNextWindowPos(), as this may incur tearing and side-effects. +// Unsupported arg type ImGuiCond cond = 0 +// IMGUI_API void SetWindowSize(const ImVec2& size, ImGuiCond cond = 0); // (not recommended) set current window size - call within Begin()/End(). set to ImVec2 0 0 to force an auto-fit. prefer using SetNextWindowSize(), as this may incur tearing and minor side-effects. +// Unsupported arg type ImGuiCond cond = 0 +// IMGUI_API void SetWindowCollapsed(bool collapsed, ImGuiCond cond = 0); // (not recommended) set current window collapsed state. prefer using SetNextWindowCollapsed(). +// Unsupported arg type ImGuiCond cond = 0 +// IMGUI_API void SetWindowFocus(); // (not recommended) set current window to be focused / front-most. prefer using SetNextWindowFocus(). +IMGUI_FUNCTION(SetWindowFocus) +CALL_FUNCTION_NO_RET(SetWindowFocus) +END_IMGUI_FUNC +// IMGUI_API void SetWindowFontScale(float scale); // set font scale. Adjust IO.FontGlobalScale if you want to scale all windows +IMGUI_FUNCTION(SetWindowFontScale) +NUMBER_ARG(scale) +CALL_FUNCTION_NO_RET(SetWindowFontScale, scale) +END_IMGUI_FUNC +// IMGUI_API void SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond = 0); // set named window position. +// Unsupported arg type ImGuiCond cond = 0 +// IMGUI_API void SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond = 0); // set named window size. set axis to 0.0f to force an auto-fit on this axis. +// Unsupported arg type ImGuiCond cond = 0 +// IMGUI_API void SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond = 0); // set named window collapsed state +// Unsupported arg type ImGuiCond cond = 0 +// IMGUI_API void SetWindowFocus(const char* name); // set named window to be focused / front-most. use NULL to remove focus. +IMGUI_FUNCTION(SetWindowFocus_1) +LABEL_ARG(name) +CALL_FUNCTION_NO_RET(SetWindowFocus, name) +END_IMGUI_FUNC +// IMGUI_API float GetScrollX(); // get scrolling amount [0..GetScrollMaxX()] +IMGUI_FUNCTION(GetScrollX) +CALL_FUNCTION(GetScrollX, float) +PUSH_NUMBER(ret) +END_IMGUI_FUNC +// IMGUI_API float GetScrollY(); // get scrolling amount [0..GetScrollMaxY()] +IMGUI_FUNCTION(GetScrollY) +CALL_FUNCTION(GetScrollY, float) +PUSH_NUMBER(ret) +END_IMGUI_FUNC +// IMGUI_API float GetScrollMaxX(); // get maximum scrolling amount ~~ ContentSize.X - WindowSize.X +IMGUI_FUNCTION(GetScrollMaxX) +CALL_FUNCTION(GetScrollMaxX, float) +PUSH_NUMBER(ret) +END_IMGUI_FUNC +// IMGUI_API float GetScrollMaxY(); // get maximum scrolling amount ~~ ContentSize.Y - WindowSize.Y +IMGUI_FUNCTION(GetScrollMaxY) +CALL_FUNCTION(GetScrollMaxY, float) +PUSH_NUMBER(ret) +END_IMGUI_FUNC +// IMGUI_API void SetScrollX(float scroll_x); // set scrolling amount [0..GetScrollMaxX()] +IMGUI_FUNCTION(SetScrollX) +NUMBER_ARG(scroll_x) +CALL_FUNCTION_NO_RET(SetScrollX, scroll_x) +END_IMGUI_FUNC +// IMGUI_API void SetScrollY(float scroll_y); // set scrolling amount [0..GetScrollMaxY()] +IMGUI_FUNCTION(SetScrollY) +NUMBER_ARG(scroll_y) +CALL_FUNCTION_NO_RET(SetScrollY, scroll_y) +END_IMGUI_FUNC +// IMGUI_API void SetScrollHere(float center_y_ratio = 0.5f); // adjust scrolling amount to make current cursor position visible. center_y_ratio=0.0: top, 0.5: center, 1.0: bottom. When using to make a "default/current item" visible, consider using SetItemDefaultFocus() instead. +IMGUI_FUNCTION(SetScrollHere) +OPTIONAL_NUMBER_ARG(center_y_ratio, 0.5f) +CALL_FUNCTION_NO_RET(SetScrollHere, center_y_ratio) +END_IMGUI_FUNC +// IMGUI_API void SetScrollFromPosY(float pos_y, float center_y_ratio = 0.5f); // adjust scrolling amount to make given position valid. use GetCursorPos() or GetCursorStartPos()+offset to get valid positions. +IMGUI_FUNCTION(SetScrollFromPosY) +NUMBER_ARG(pos_y) +OPTIONAL_NUMBER_ARG(center_y_ratio, 0.5f) +CALL_FUNCTION_NO_RET(SetScrollFromPosY, pos_y, center_y_ratio) +END_IMGUI_FUNC +// IMGUI_API void PushFont(ImFont* font); // use NULL as a shortcut to push default font +// Unsupported arg type ImFont* font +// IMGUI_API void PopFont(); +IMGUI_FUNCTION(PopFont) +CALL_FUNCTION_NO_RET(PopFont) +END_IMGUI_FUNC +// IMGUI_API void PushStyleColor(ImGuiCol idx, ImU32 col); +IMGUI_FUNCTION(PushStyleColor) +INT_ARG(idx) +UINT_ARG(col) +CALL_FUNCTION_NO_RET(PushStyleColor, idx, col) +END_IMGUI_FUNC +// IMGUI_API void PushStyleColor(ImGuiCol idx, const ImVec4& col); +IMGUI_FUNCTION(PushStyleColor_2) +INT_ARG(idx) +IM_VEC_4_ARG(col) +CALL_FUNCTION_NO_RET(PushStyleColor, idx, col) +END_IMGUI_FUNC +// IMGUI_API void PopStyleColor(int count = 1); +IMGUI_FUNCTION(PopStyleColor) +OPTIONAL_INT_ARG(count, 1) +CALL_FUNCTION_NO_RET(PopStyleColor, count) +END_IMGUI_FUNC +// IMGUI_API void PushStyleVar(ImGuiStyleVar idx, float val); +IMGUI_FUNCTION(PushStyleVar) +INT_ARG(idx) +NUMBER_ARG(val) +CALL_FUNCTION_NO_RET(PushStyleVar, idx, val) +ADD_END_STACK(3) +END_IMGUI_FUNC +// IMGUI_API void PushStyleVar(ImGuiStyleVar idx, const ImVec2& val); +IMGUI_FUNCTION(PushStyleVar_2) +INT_ARG(idx) +IM_VEC_2_ARG(val) +CALL_FUNCTION_NO_RET(PushStyleVar, idx, val) +ADD_END_STACK(3) +END_IMGUI_FUNC +// IMGUI_API void PopStyleVar(int count = 1); +IMGUI_FUNCTION(PopStyleVar) +OPTIONAL_INT_ARG(count, 1) +CALL_FUNCTION_NO_RET(PopStyleVar, count) +POP_END_STACK(3) +END_IMGUI_FUNC +// IMGUI_API const ImVec4& GetStyleColorVec4(ImGuiCol idx); // retrieve style color as stored in ImGuiStyle structure. use to feed back into PushStyleColor(), otherwhise use GetColorU32() to get style color with style alpha baked in. +// Unsupported return type const +// IMGUI_API ImFont* GetFont(); // get current font +// Unsupported return type ImFont* +// IMGUI_API float GetFontSize(); // get current font size (= height in pixels) of current font with current scale applied +IMGUI_FUNCTION(GetFontSize) +CALL_FUNCTION(GetFontSize, float) +PUSH_NUMBER(ret) +END_IMGUI_FUNC +// IMGUI_API ImVec2 GetFontTexUvWhitePixel(); // get UV coordinate for a while pixel, useful to draw custom shapes via the ImDrawList API +IMGUI_FUNCTION(GetFontTexUvWhitePixel) +CALL_FUNCTION(GetFontTexUvWhitePixel, ImVec2) +PUSH_NUMBER(ret.x) +PUSH_NUMBER(ret.y) +END_IMGUI_FUNC +// IMGUI_API ImU32 GetColorU32(ImGuiCol idx, float alpha_mul = 1.0f); // retrieve given style color with style alpha applied and optional extra alpha multiplier +IMGUI_FUNCTION(GetColorU32) +INT_ARG(idx) +OPTIONAL_NUMBER_ARG(alpha_mul, 1.0f) +CALL_FUNCTION(GetColorU32, unsigned int, idx, alpha_mul) +PUSH_NUMBER(ret) +END_IMGUI_FUNC +// IMGUI_API ImU32 GetColorU32(const ImVec4& col); // retrieve given color with style alpha applied +IMGUI_FUNCTION(GetColorU32_1) +IM_VEC_4_ARG(col) +CALL_FUNCTION(GetColorU32, unsigned int, col) +PUSH_NUMBER(ret) +END_IMGUI_FUNC +// IMGUI_API ImU32 GetColorU32(ImU32 col); // retrieve given color with style alpha applied +IMGUI_FUNCTION(GetColorU32_1_1) +UINT_ARG(col) +CALL_FUNCTION(GetColorU32, unsigned int, col) +PUSH_NUMBER(ret) +END_IMGUI_FUNC +// IMGUI_API void PushItemWidth(float item_width); // width of items for the common item+label case, pixels. 0.0f = default to ~2/3 of windows width, >0.0f: width in pixels, <0.0f align xx pixels to the right of window (so -1.0f always align width to the right side) +IMGUI_FUNCTION(PushItemWidth) +NUMBER_ARG(item_width) +CALL_FUNCTION_NO_RET(PushItemWidth, item_width) +END_IMGUI_FUNC +// IMGUI_API void PopItemWidth(); +IMGUI_FUNCTION(PopItemWidth) +CALL_FUNCTION_NO_RET(PopItemWidth) +END_IMGUI_FUNC +// IMGUI_API float CalcItemWidth(); // width of item given pushed settings and current cursor position +IMGUI_FUNCTION(CalcItemWidth) +CALL_FUNCTION(CalcItemWidth, float) +PUSH_NUMBER(ret) +END_IMGUI_FUNC +// IMGUI_API void PushTextWrapPos(float wrap_pos_x = 0.0f); // word-wrapping for Text*() commands. < 0.0f: no wrapping; 0.0f: wrap to end of window (or column); > 0.0f: wrap at 'wrap_pos_x' position in window local space +IMGUI_FUNCTION(PushTextWrapPos) +OPTIONAL_NUMBER_ARG(wrap_pos_x, 0.0f) +CALL_FUNCTION_NO_RET(PushTextWrapPos, wrap_pos_x) +END_IMGUI_FUNC +// IMGUI_API void PopTextWrapPos(); +IMGUI_FUNCTION(PopTextWrapPos) +CALL_FUNCTION_NO_RET(PopTextWrapPos) +END_IMGUI_FUNC +// IMGUI_API void PushAllowKeyboardFocus(bool allow_keyboard_focus); // allow focusing using TAB/Shift-TAB, enabled by default but you can disable it for certain widgets +IMGUI_FUNCTION(PushAllowKeyboardFocus) +BOOL_ARG(allow_keyboard_focus) +CALL_FUNCTION_NO_RET(PushAllowKeyboardFocus, allow_keyboard_focus) +END_IMGUI_FUNC +// IMGUI_API void PopAllowKeyboardFocus(); +IMGUI_FUNCTION(PopAllowKeyboardFocus) +CALL_FUNCTION_NO_RET(PopAllowKeyboardFocus) +END_IMGUI_FUNC +// IMGUI_API void PushButtonRepeat(bool repeat); // in 'repeat' mode, Button*() functions return repeated true in a typematic manner (using io.KeyRepeatDelay/io.KeyRepeatRate setting). Note that you can call IsItemActive() after any Button() to tell if the button is held in the current frame. +IMGUI_FUNCTION(PushButtonRepeat) +BOOL_ARG(repeat) +CALL_FUNCTION_NO_RET(PushButtonRepeat, repeat) +END_IMGUI_FUNC +// IMGUI_API void PopButtonRepeat(); +IMGUI_FUNCTION(PopButtonRepeat) +CALL_FUNCTION_NO_RET(PopButtonRepeat) +END_IMGUI_FUNC +// IMGUI_API void Separator(); // separator, generally horizontal. inside a menu bar or in horizontal layout mode, this becomes a vertical separator. +IMGUI_FUNCTION(Separator) +CALL_FUNCTION_NO_RET(Separator) +END_IMGUI_FUNC +// IMGUI_API void SameLine(float pos_x = 0.0f, float spacing_w = -1.0f); // call between widgets or groups to layout them horizontally +IMGUI_FUNCTION(SameLine) +OPTIONAL_NUMBER_ARG(pos_x, 0.0f) +OPTIONAL_NUMBER_ARG(spacing_w, -1.0f) +CALL_FUNCTION_NO_RET(SameLine, pos_x, spacing_w) +END_IMGUI_FUNC +// IMGUI_API void NewLine(); // undo a SameLine() +IMGUI_FUNCTION(NewLine) +CALL_FUNCTION_NO_RET(NewLine) +END_IMGUI_FUNC +// IMGUI_API void Spacing(); // add vertical spacing +IMGUI_FUNCTION(Spacing) +CALL_FUNCTION_NO_RET(Spacing) +END_IMGUI_FUNC +// IMGUI_API void Dummy(const ImVec2& size); // add a dummy item of given size +IMGUI_FUNCTION(Dummy) +IM_VEC_2_ARG(size) +CALL_FUNCTION_NO_RET(Dummy, size) +END_IMGUI_FUNC +// IMGUI_API void Indent(float indent_w = 0.0f); // move content position toward the right, by style.IndentSpacing or indent_w if != 0 +IMGUI_FUNCTION(Indent) +OPTIONAL_NUMBER_ARG(indent_w, 0.0f) +CALL_FUNCTION_NO_RET(Indent, indent_w) +END_IMGUI_FUNC +// IMGUI_API void Unindent(float indent_w = 0.0f); // move content position back to the left, by style.IndentSpacing or indent_w if != 0 +IMGUI_FUNCTION(Unindent) +OPTIONAL_NUMBER_ARG(indent_w, 0.0f) +CALL_FUNCTION_NO_RET(Unindent, indent_w) +END_IMGUI_FUNC +// IMGUI_API void BeginGroup(); // lock horizontal starting position + capture group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.) +IMGUI_FUNCTION(BeginGroup) +CALL_FUNCTION_NO_RET(BeginGroup) +ADD_END_STACK(4) +END_IMGUI_FUNC +// IMGUI_API void EndGroup(); +IMGUI_FUNCTION(EndGroup) +CALL_FUNCTION_NO_RET(EndGroup) +POP_END_STACK(4) +END_IMGUI_FUNC +// IMGUI_API ImVec2 GetCursorPos(); // cursor position is relative to window position +IMGUI_FUNCTION(GetCursorPos) +CALL_FUNCTION(GetCursorPos, ImVec2) +PUSH_NUMBER(ret.x) +PUSH_NUMBER(ret.y) +END_IMGUI_FUNC +// IMGUI_API float GetCursorPosX(); // " +IMGUI_FUNCTION(GetCursorPosX) +CALL_FUNCTION(GetCursorPosX, float) +PUSH_NUMBER(ret) +END_IMGUI_FUNC +// IMGUI_API float GetCursorPosY(); // " +IMGUI_FUNCTION(GetCursorPosY) +CALL_FUNCTION(GetCursorPosY, float) +PUSH_NUMBER(ret) +END_IMGUI_FUNC +// IMGUI_API void SetCursorPos(const ImVec2& local_pos); // " +IMGUI_FUNCTION(SetCursorPos) +IM_VEC_2_ARG(local_pos) +CALL_FUNCTION_NO_RET(SetCursorPos, local_pos) +END_IMGUI_FUNC +// IMGUI_API void SetCursorPosX(float x); // " +IMGUI_FUNCTION(SetCursorPosX) +NUMBER_ARG(x) +CALL_FUNCTION_NO_RET(SetCursorPosX, x) +END_IMGUI_FUNC +// IMGUI_API void SetCursorPosY(float y); // " +IMGUI_FUNCTION(SetCursorPosY) +NUMBER_ARG(y) +CALL_FUNCTION_NO_RET(SetCursorPosY, y) +END_IMGUI_FUNC +// IMGUI_API ImVec2 GetCursorStartPos(); // initial cursor position +IMGUI_FUNCTION(GetCursorStartPos) +CALL_FUNCTION(GetCursorStartPos, ImVec2) +PUSH_NUMBER(ret.x) +PUSH_NUMBER(ret.y) +END_IMGUI_FUNC +// IMGUI_API ImVec2 GetCursorScreenPos(); // cursor position in absolute screen coordinates [0..io.DisplaySize] (useful to work with ImDrawList API) +IMGUI_FUNCTION(GetCursorScreenPos) +CALL_FUNCTION(GetCursorScreenPos, ImVec2) +PUSH_NUMBER(ret.x) +PUSH_NUMBER(ret.y) +END_IMGUI_FUNC +// IMGUI_API void SetCursorScreenPos(const ImVec2& screen_pos); // cursor position in absolute screen coordinates [0..io.DisplaySize] +IMGUI_FUNCTION(SetCursorScreenPos) +IM_VEC_2_ARG(screen_pos) +CALL_FUNCTION_NO_RET(SetCursorScreenPos, screen_pos) +END_IMGUI_FUNC +// IMGUI_API void AlignTextToFramePadding(); // vertically align upcoming text baseline to FramePadding.y so that it will align properly to regularly framed items (call if you have text on a line before a framed item) +IMGUI_FUNCTION(AlignTextToFramePadding) +CALL_FUNCTION_NO_RET(AlignTextToFramePadding) +END_IMGUI_FUNC +// IMGUI_API float GetTextLineHeight(); // ~ FontSize +IMGUI_FUNCTION(GetTextLineHeight) +CALL_FUNCTION(GetTextLineHeight, float) +PUSH_NUMBER(ret) +END_IMGUI_FUNC +// IMGUI_API float GetTextLineHeightWithSpacing(); // ~ FontSize + style.ItemSpacing.y (distance in pixels between 2 consecutive lines of text) +IMGUI_FUNCTION(GetTextLineHeightWithSpacing) +CALL_FUNCTION(GetTextLineHeightWithSpacing, float) +PUSH_NUMBER(ret) +END_IMGUI_FUNC +// IMGUI_API float GetFrameHeight(); // ~ FontSize + style.FramePadding.y * 2 +IMGUI_FUNCTION(GetFrameHeight) +CALL_FUNCTION(GetFrameHeight, float) +PUSH_NUMBER(ret) +END_IMGUI_FUNC +// IMGUI_API float GetFrameHeightWithSpacing(); // ~ FontSize + style.FramePadding.y * 2 + style.ItemSpacing.y (distance in pixels between 2 consecutive lines of framed widgets) +IMGUI_FUNCTION(GetFrameHeightWithSpacing) +CALL_FUNCTION(GetFrameHeightWithSpacing, float) +PUSH_NUMBER(ret) +END_IMGUI_FUNC +// IMGUI_API void PushID(const char* str_id); // push identifier into the ID stack. IDs are hash of the entire stack! +IMGUI_FUNCTION(PushID) +LABEL_ARG(str_id) +CALL_FUNCTION_NO_RET(PushID, str_id) +END_IMGUI_FUNC +// IMGUI_API void PushID(const char* str_id_begin, const char* str_id_end); +IMGUI_FUNCTION(PushID_2) +LABEL_ARG(str_id_begin) +LABEL_ARG(str_id_end) +CALL_FUNCTION_NO_RET(PushID, str_id_begin, str_id_end) +END_IMGUI_FUNC +// IMGUI_API void PushID(const void* ptr_id); +// Unsupported arg type const void* ptr_id +// IMGUI_API void PushID(int int_id); +IMGUI_FUNCTION(PushID_1) +INT_ARG(int_id) +CALL_FUNCTION_NO_RET(PushID, int_id) +END_IMGUI_FUNC +// IMGUI_API void PopID(); +IMGUI_FUNCTION(PopID) +CALL_FUNCTION_NO_RET(PopID) +END_IMGUI_FUNC +// IMGUI_API ImGuiID GetID(const char* str_id); // calculate unique ID (hash of whole ID stack + given parameter). e.g. if you want to query into ImGuiStorage yourself +IMGUI_FUNCTION(GetID) +LABEL_ARG(str_id) +CALL_FUNCTION(GetID, unsigned int, str_id) +PUSH_NUMBER(ret) +END_IMGUI_FUNC +// IMGUI_API ImGuiID GetID(const char* str_id_begin, const char* str_id_end); +IMGUI_FUNCTION(GetID_2) +LABEL_ARG(str_id_begin) +LABEL_ARG(str_id_end) +CALL_FUNCTION(GetID, unsigned int, str_id_begin, str_id_end) +PUSH_NUMBER(ret) +END_IMGUI_FUNC +// IMGUI_API ImGuiID GetID(const void* ptr_id); +// Unsupported arg type const void* ptr_id +// IMGUI_API void TextUnformatted(const char* text, const char* text_end = NULL); // raw text without formatting. Roughly equivalent to Text("%s", text) but: A) doesn't require null terminated string if 'text_end' is specified, B) it's faster, no memory copy is done, no buffer size limits, recommended for long chunks of text. +IMGUI_FUNCTION(TextUnformatted) +LABEL_ARG(text) +OPTIONAL_LABEL_ARG(text_end) +CALL_FUNCTION_NO_RET(TextUnformatted, text, text_end) +END_IMGUI_FUNC +// IMGUI_API void Text(const char* fmt, ...) IM_FMTARGS(1); // simple formatted text +// Unsupported arg type ...) IM_FMTARGS(1 +// IMGUI_API void TextV(const char* fmt, va_list args) IM_FMTLIST(1); +// Unsupported arg type va_list args) IM_FMTLIST(1 +// IMGUI_API void TextColored(const ImVec4& col, const char* fmt, ...) IM_FMTARGS(2); // shortcut for PushStyleColor(ImGuiCol_Text, col); Text(fmt, ...); PopStyleColor(); +// Unsupported arg type ...) IM_FMTARGS(2 +// IMGUI_API void TextColoredV(const ImVec4& col, const char* fmt, va_list args) IM_FMTLIST(2); +// Unsupported arg type va_list args) IM_FMTLIST(2 +// IMGUI_API void TextDisabled(const char* fmt, ...) IM_FMTARGS(1); // shortcut for PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_TextDisabled]); Text(fmt, ...); PopStyleColor(); +// Unsupported arg type ...) IM_FMTARGS(1 +// IMGUI_API void TextDisabledV(const char* fmt, va_list args) IM_FMTLIST(1); +// Unsupported arg type va_list args) IM_FMTLIST(1 +// IMGUI_API void TextWrapped(const char* fmt, ...) IM_FMTARGS(1); // shortcut for PushTextWrapPos(0.0f); Text(fmt, ...); PopTextWrapPos();. Note that this won't work on an auto-resizing window if there's no other widgets to extend the window width, yoy may need to set a size using SetNextWindowSize(). +// Unsupported arg type ...) IM_FMTARGS(1 +// IMGUI_API void TextWrappedV(const char* fmt, va_list args) IM_FMTLIST(1); +// Unsupported arg type va_list args) IM_FMTLIST(1 +// IMGUI_API void LabelText(const char* label, const char* fmt, ...) IM_FMTARGS(2); // display text+label aligned the same way as value+label widgets +// Unsupported arg type ...) IM_FMTARGS(2 +// IMGUI_API void LabelTextV(const char* label, const char* fmt, va_list args) IM_FMTLIST(2); +// Unsupported arg type va_list args) IM_FMTLIST(2 +// IMGUI_API void BulletText(const char* fmt, ...) IM_FMTARGS(1); // shortcut for Bullet()+Text() +// Unsupported arg type ...) IM_FMTARGS(1 +// IMGUI_API void BulletTextV(const char* fmt, va_list args) IM_FMTLIST(1); +// Unsupported arg type va_list args) IM_FMTLIST(1 +// IMGUI_API bool Button(const char* label, const ImVec2& size = ImVec2 0 0); // button +IMGUI_FUNCTION(Button) +LABEL_ARG(label) +OPTIONAL_IM_VEC_2_ARG(size, 0, 0) +CALL_FUNCTION(Button, bool, label, size) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API bool SmallButton(const char* label); // button with FramePadding=(0,0) to easily embed within text +IMGUI_FUNCTION(SmallButton) +LABEL_ARG(label) +CALL_FUNCTION(SmallButton, bool, label) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API bool ArrowButton(const char* str_id, ImGuiDir dir); +// Unsupported arg type ImGuiDir dir +// IMGUI_API bool InvisibleButton(const char* str_id, const ImVec2& size); // button behavior without the visuals, useful to build custom behaviors using the public api (along with IsItemActive, IsItemHovered, etc.) +IMGUI_FUNCTION(InvisibleButton) +LABEL_ARG(str_id) +IM_VEC_2_ARG(size) +CALL_FUNCTION(InvisibleButton, bool, str_id, size) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API void Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2 0 0, const ImVec2& uv1 = ImVec2 1 1, const ImVec4& tint_col = ImVec4 1 1 1 1, const ImVec4& border_col = ImVec4 0 0 0 0); +IMGUI_FUNCTION(Image) +IM_TEXTURE_ID_ARG(user_texture_id) +IM_VEC_2_ARG(size) +OPTIONAL_IM_VEC_2_ARG(uv0, 0, 0) +OPTIONAL_IM_VEC_2_ARG(uv1, 1, 1) +OPTIONAL_IM_VEC_4_ARG(tint_col, 1, 1, 1, 1) +OPTIONAL_IM_VEC_4_ARG(border_col, 0, 0, 0, 0) +CALL_FUNCTION_NO_RET(Image, user_texture_id, size, uv0, uv1, tint_col, border_col) +END_IMGUI_FUNC +// IMGUI_API bool ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2 0 0, const ImVec2& uv1 = ImVec2 1 1, int frame_padding = -1, const ImVec4& bg_col = ImVec4 0 0 0 0, const ImVec4& tint_col = ImVec4 1 1 1 1); // <0 frame_padding uses default frame padding settings. 0 for no padding +IMGUI_FUNCTION(ImageButton) +IM_TEXTURE_ID_ARG(user_texture_id) +IM_VEC_2_ARG(size) +OPTIONAL_IM_VEC_2_ARG(uv0, 0, 0) +OPTIONAL_IM_VEC_2_ARG(uv1, 1, 1) +OPTIONAL_INT_ARG(frame_padding, -1) +OPTIONAL_IM_VEC_4_ARG(bg_col, 0, 0, 0, 0) +OPTIONAL_IM_VEC_4_ARG(tint_col, 1, 1, 1, 1) +CALL_FUNCTION(ImageButton, bool, user_texture_id, size, uv0, uv1, frame_padding, bg_col, tint_col) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API bool Checkbox(const char* label, bool* v); +IMGUI_FUNCTION(Checkbox) +LABEL_ARG(label) +BOOL_POINTER_ARG(v) +CALL_FUNCTION(Checkbox, bool, label, v) +PUSH_BOOL(ret) +END_BOOL_POINTER(v) +END_IMGUI_FUNC +// IMGUI_API bool CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value); +IMGUI_FUNCTION(CheckboxFlags) +LABEL_ARG(label) +UINT_POINTER_ARG(flags) +UINT_ARG(flags_value) +CALL_FUNCTION(CheckboxFlags, bool, label, flags, flags_value) +PUSH_BOOL(ret) +END_UINT_POINTER(flags) +END_IMGUI_FUNC +// IMGUI_API bool RadioButton(const char* label, bool active); +IMGUI_FUNCTION(RadioButton) +LABEL_ARG(label) +BOOL_ARG(active) +CALL_FUNCTION(RadioButton, bool, label, active) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API bool RadioButton(const char* label, int* v, int v_button); +IMGUI_FUNCTION(RadioButton_3) +LABEL_ARG(label) +INT_POINTER_ARG(v) +INT_ARG(v_button) +CALL_FUNCTION(RadioButton, bool, label, v, v_button) +PUSH_BOOL(ret) +END_INT_POINTER(v) +END_IMGUI_FUNC +// IMGUI_API void PlotLines(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2 0 0, int stride = sizeof(float)); +// Unsupported arg type const float* values +// Unsupported arg type ImVec2 graph_size = ImVec2 0 0 +// IMGUI_API void PlotLines(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2 0 0); +// Unsupported arg type float (*values_getter)(void* data +// Unsupported arg type void* data +// Unsupported arg type ImVec2 graph_size = ImVec2 0 0 +// IMGUI_API void PlotHistogram(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2 0 0, int stride = sizeof(float)); +// Unsupported arg type const float* values +// Unsupported arg type ImVec2 graph_size = ImVec2 0 0 +// IMGUI_API void PlotHistogram(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2 0 0); +// Unsupported arg type float (*values_getter)(void* data +// Unsupported arg type void* data +// Unsupported arg type ImVec2 graph_size = ImVec2 0 0 +// IMGUI_API void ProgressBar(float fraction, const ImVec2& size_arg = ImVec2 -1 0, const char* overlay = NULL); +IMGUI_FUNCTION(ProgressBar) +NUMBER_ARG(fraction) +OPTIONAL_IM_VEC_2_ARG(size_arg, -1, 0) +OPTIONAL_LABEL_ARG(overlay) +CALL_FUNCTION_NO_RET(ProgressBar, fraction, size_arg, overlay) +END_IMGUI_FUNC +// IMGUI_API void Bullet(); // draw a small circle and keep the cursor on the same line. advance cursor x position by GetTreeNodeToLabelSpacing(), same distance that TreeNode() uses +IMGUI_FUNCTION(Bullet) +CALL_FUNCTION_NO_RET(Bullet) +END_IMGUI_FUNC +// IMGUI_API bool BeginCombo(const char* label, const char* preview_value, ImGuiComboFlags flags = 0); +// Unsupported arg type ImGuiComboFlags flags = 0 +// IMGUI_API void EndCombo(); // only call EndCombo() if BeginCombo() returns true! +IMGUI_FUNCTION(EndCombo) +CALL_FUNCTION_NO_RET(EndCombo) +POP_END_STACK(5) +END_IMGUI_FUNC +// IMGUI_API bool Combo(const char* label, int* current_item, const char* const items[], int items_count, int popup_max_height_in_items = -1); +// Unsupported arg type const char* const items[] +// IMGUI_API bool Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int popup_max_height_in_items = -1); // Separate items with \0 within a string, end item-list with \0\0. e.g. "One\0Two\0Three\0" +IMGUI_FUNCTION(Combo) +LABEL_ARG(label) +INT_POINTER_ARG(current_item) +LABEL_ARG(items_separated_by_zeros) +OPTIONAL_INT_ARG(popup_max_height_in_items, -1) +CALL_FUNCTION(Combo, bool, label, current_item, items_separated_by_zeros, popup_max_height_in_items) +PUSH_BOOL(ret) +END_INT_POINTER(current_item) +END_IMGUI_FUNC +// IMGUI_API bool Combo(const char* label, int* current_item, bool(*items_getter)(void* data, int idx, const char** out_text), void* data, int items_count, int popup_max_height_in_items = -1); +// Unsupported arg type bool(*items_getter)(void* data +// Unsupported arg type const char** out_text) +// Unsupported arg type void* data +// IMGUI_API bool DragFloat(const char* label, float* v, float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", float power = 1.0f); // If v_min >= v_max we have no bound +IMGUI_FUNCTION(DragFloat) +LABEL_ARG(label) +FLOAT_POINTER_ARG(v) +OPTIONAL_NUMBER_ARG(v_speed, 1.0f) +OPTIONAL_NUMBER_ARG(v_min, 0.0f) +OPTIONAL_NUMBER_ARG(v_max, 0.0f) +LABEL_ARG(display_format) +OPTIONAL_NUMBER_ARG(power, 1.0f) +CALL_FUNCTION(DragFloat, bool, label, v, v_speed, v_min, v_max, display_format, power) +PUSH_BOOL(ret) +END_FLOAT_POINTER(v) +END_IMGUI_FUNC +// IMGUI_API bool DragFloat2(const char* label, float v[2], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", float power = 1.0f); +// Unsupported arg type float v[2] +// IMGUI_API bool DragFloat3(const char* label, float v[3], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", float power = 1.0f); +// Unsupported arg type float v[3] +// IMGUI_API bool DragFloat4(const char* label, float v[4], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", float power = 1.0f); +// Unsupported arg type float v[4] +// IMGUI_API bool DragFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", const char* display_format_max = NULL, float power = 1.0f); +IMGUI_FUNCTION(DragFloatRange2) +LABEL_ARG(label) +FLOAT_POINTER_ARG(v_current_min) +FLOAT_POINTER_ARG(v_current_max) +OPTIONAL_NUMBER_ARG(v_speed, 1.0f) +OPTIONAL_NUMBER_ARG(v_min, 0.0f) +OPTIONAL_NUMBER_ARG(v_max, 0.0f) +LABEL_ARG(display_format) +OPTIONAL_LABEL_ARG(display_format_max) +OPTIONAL_NUMBER_ARG(power, 1.0f) +CALL_FUNCTION(DragFloatRange2, bool, label, v_current_min, v_current_max, v_speed, v_min, v_max, display_format, display_format_max, power) +PUSH_BOOL(ret) +END_FLOAT_POINTER(v_current_min) +END_FLOAT_POINTER(v_current_max) +END_IMGUI_FUNC +// IMGUI_API bool DragInt(const char* label, int* v, float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* display_format = "%.0f"); // If v_min >= v_max we have no bound +IMGUI_FUNCTION(DragInt) +LABEL_ARG(label) +INT_POINTER_ARG(v) +OPTIONAL_NUMBER_ARG(v_speed, 1.0f) +OPTIONAL_INT_ARG(v_min, 0) +OPTIONAL_INT_ARG(v_max, 0) +LABEL_ARG(display_format) +CALL_FUNCTION(DragInt, bool, label, v, v_speed, v_min, v_max, display_format) +PUSH_BOOL(ret) +END_INT_POINTER(v) +END_IMGUI_FUNC +// IMGUI_API bool DragInt2(const char* label, int v[2], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* display_format = "%.0f"); +// Unsupported arg type int v[2] +// IMGUI_API bool DragInt3(const char* label, int v[3], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* display_format = "%.0f"); +// Unsupported arg type int v[3] +// IMGUI_API bool DragInt4(const char* label, int v[4], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* display_format = "%.0f"); +// Unsupported arg type int v[4] +// IMGUI_API bool DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* display_format = "%.0f", const char* display_format_max = NULL); +IMGUI_FUNCTION(DragIntRange2) +LABEL_ARG(label) +INT_POINTER_ARG(v_current_min) +INT_POINTER_ARG(v_current_max) +OPTIONAL_NUMBER_ARG(v_speed, 1.0f) +OPTIONAL_INT_ARG(v_min, 0) +OPTIONAL_INT_ARG(v_max, 0) +LABEL_ARG(display_format) +OPTIONAL_LABEL_ARG(display_format_max) +CALL_FUNCTION(DragIntRange2, bool, label, v_current_min, v_current_max, v_speed, v_min, v_max, display_format, display_format_max) +PUSH_BOOL(ret) +END_INT_POINTER(v_current_min) +END_INT_POINTER(v_current_max) +END_IMGUI_FUNC +// IMGUI_API bool InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, ImGuiTextEditCallback callback = NULL, void* user_data = NULL); +// Unsupported arg type char* buf +// Unsupported arg type size_t buf_size +// Unsupported arg type ImGuiTextEditCallback callback = NULL +// Unsupported arg type void* user_data = NULL +// IMGUI_API bool InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size = ImVec2 0 0, ImGuiInputTextFlags flags = 0, ImGuiTextEditCallback callback = NULL, void* user_data = NULL); +// Unsupported arg type char* buf +// Unsupported arg type size_t buf_size +// Unsupported arg type ImGuiTextEditCallback callback = NULL +// Unsupported arg type void* user_data = NULL +// IMGUI_API bool InputFloat(const char* label, float* v, float step = 0.0f, float step_fast = 0.0f, int decimal_precision = -1, ImGuiInputTextFlags extra_flags = 0); +IMGUI_FUNCTION(InputFloat) +LABEL_ARG(label) +FLOAT_POINTER_ARG(v) +OPTIONAL_NUMBER_ARG(step, 0.0f) +OPTIONAL_NUMBER_ARG(step_fast, 0.0f) +OPTIONAL_INT_ARG(decimal_precision, -1) +OPTIONAL_INT_ARG(extra_flags, 0) +CALL_FUNCTION(InputFloat, bool, label, v, step, step_fast, decimal_precision, extra_flags) +PUSH_BOOL(ret) +END_FLOAT_POINTER(v) +END_IMGUI_FUNC +// IMGUI_API bool InputFloat2(const char* label, float v[2], int decimal_precision = -1, ImGuiInputTextFlags extra_flags = 0); +// Unsupported arg type float v[2] +// IMGUI_API bool InputFloat3(const char* label, float v[3], int decimal_precision = -1, ImGuiInputTextFlags extra_flags = 0); +// Unsupported arg type float v[3] +// IMGUI_API bool InputFloat4(const char* label, float v[4], int decimal_precision = -1, ImGuiInputTextFlags extra_flags = 0); +// Unsupported arg type float v[4] +// IMGUI_API bool InputInt(const char* label, int* v, int step = 1, int step_fast = 100, ImGuiInputTextFlags extra_flags = 0); +IMGUI_FUNCTION(InputInt) +LABEL_ARG(label) +INT_POINTER_ARG(v) +OPTIONAL_INT_ARG(step, 1) +OPTIONAL_INT_ARG(step_fast, 100) +OPTIONAL_INT_ARG(extra_flags, 0) +CALL_FUNCTION(InputInt, bool, label, v, step, step_fast, extra_flags) +PUSH_BOOL(ret) +END_INT_POINTER(v) +END_IMGUI_FUNC +// IMGUI_API bool InputInt2(const char* label, int v[2], ImGuiInputTextFlags extra_flags = 0); +// Unsupported arg type int v[2] +// IMGUI_API bool InputInt3(const char* label, int v[3], ImGuiInputTextFlags extra_flags = 0); +// Unsupported arg type int v[3] +// IMGUI_API bool InputInt4(const char* label, int v[4], ImGuiInputTextFlags extra_flags = 0); +// Unsupported arg type int v[4] +// IMGUI_API bool InputDouble(const char* label, double* v, double step = 0.0f, double step_fast = 0.0f, const char* display_format = "%.6f", ImGuiInputTextFlags extra_flags = 0); +// Unsupported arg type double* v +// Unsupported arg type double step = 0.0f +// Unsupported arg type double step_fast = 0.0f +// IMGUI_API bool SliderFloat(const char* label, float* v, float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); // adjust display_format to decorate the value with a prefix or a suffix for in-slider labels or unit display. Use power!=1.0 for logarithmic sliders +IMGUI_FUNCTION(SliderFloat) +LABEL_ARG(label) +FLOAT_POINTER_ARG(v) +NUMBER_ARG(v_min) +NUMBER_ARG(v_max) +LABEL_ARG(display_format) +OPTIONAL_NUMBER_ARG(power, 1.0f) +CALL_FUNCTION(SliderFloat, bool, label, v, v_min, v_max, display_format, power) +PUSH_BOOL(ret) +END_FLOAT_POINTER(v) +END_IMGUI_FUNC +// IMGUI_API bool SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); +// Unsupported arg type float v[2] +// IMGUI_API bool SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); +// Unsupported arg type float v[3] +// IMGUI_API bool SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); +// Unsupported arg type float v[4] +// IMGUI_API bool SliderAngle(const char* label, float* v_rad, float v_degrees_min = -360.0f, float v_degrees_max = +360.0f); +IMGUI_FUNCTION(SliderAngle) +LABEL_ARG(label) +FLOAT_POINTER_ARG(v_rad) +OPTIONAL_NUMBER_ARG(v_degrees_min, -360.0f) +OPTIONAL_NUMBER_ARG(v_degrees_max, +360.0f) +CALL_FUNCTION(SliderAngle, bool, label, v_rad, v_degrees_min, v_degrees_max) +PUSH_BOOL(ret) +END_FLOAT_POINTER(v_rad) +END_IMGUI_FUNC +// IMGUI_API bool SliderInt(const char* label, int* v, int v_min, int v_max, const char* display_format = "%.0f"); +IMGUI_FUNCTION(SliderInt) +LABEL_ARG(label) +INT_POINTER_ARG(v) +INT_ARG(v_min) +INT_ARG(v_max) +LABEL_ARG(display_format) +CALL_FUNCTION(SliderInt, bool, label, v, v_min, v_max, display_format) +PUSH_BOOL(ret) +END_INT_POINTER(v) +END_IMGUI_FUNC +// IMGUI_API bool SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* display_format = "%.0f"); +// Unsupported arg type int v[2] +// IMGUI_API bool SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* display_format = "%.0f"); +// Unsupported arg type int v[3] +// IMGUI_API bool SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* display_format = "%.0f"); +// Unsupported arg type int v[4] +// IMGUI_API bool VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); +IMGUI_FUNCTION(VSliderFloat) +LABEL_ARG(label) +IM_VEC_2_ARG(size) +FLOAT_POINTER_ARG(v) +NUMBER_ARG(v_min) +NUMBER_ARG(v_max) +LABEL_ARG(display_format) +OPTIONAL_NUMBER_ARG(power, 1.0f) +CALL_FUNCTION(VSliderFloat, bool, label, size, v, v_min, v_max, display_format, power) +PUSH_BOOL(ret) +END_FLOAT_POINTER(v) +END_IMGUI_FUNC +// IMGUI_API bool VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* display_format = "%.0f"); +IMGUI_FUNCTION(VSliderInt) +LABEL_ARG(label) +IM_VEC_2_ARG(size) +INT_POINTER_ARG(v) +INT_ARG(v_min) +INT_ARG(v_max) +LABEL_ARG(display_format) +CALL_FUNCTION(VSliderInt, bool, label, size, v, v_min, v_max, display_format) +PUSH_BOOL(ret) +END_INT_POINTER(v) +END_IMGUI_FUNC +// IMGUI_API bool ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flags = 0); +// Unsupported arg type float col[3] +// Unsupported arg type ImGuiColorEditFlags flags = 0 +// IMGUI_API bool ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags = 0); +// Unsupported arg type float col[4] +// Unsupported arg type ImGuiColorEditFlags flags = 0 +// IMGUI_API bool ColorPicker3(const char* label, float col[3], ImGuiColorEditFlags flags = 0); +// Unsupported arg type float col[3] +// Unsupported arg type ImGuiColorEditFlags flags = 0 +// IMGUI_API bool ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags = 0, const float* ref_col = NULL); +// Unsupported arg type float col[4] +// Unsupported arg type ImGuiColorEditFlags flags = 0 +// Unsupported arg type const float* ref_col = NULL +// IMGUI_API bool ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags = 0, ImVec2 size = ImVec2 0 0); // display a colored square/button, hover for details, return true when pressed. +// Unsupported arg type ImGuiColorEditFlags flags = 0 +// Unsupported arg type ImVec2 size = ImVec2 0 0 +// IMGUI_API void SetColorEditOptions(ImGuiColorEditFlags flags); // initialize current options (generally on application startup) if you want to select a default format, picker type, etc. User will be able to change many settings, unless you pass the _NoOptions flag to your calls. +// Unsupported arg type ImGuiColorEditFlags flags +// IMGUI_API bool TreeNode(const char* label); // if returning 'true' the node is open and the tree id is pushed into the id stack. user is responsible for calling TreePop(). +IMGUI_FUNCTION(TreeNode) +LABEL_ARG(label) +CALL_FUNCTION(TreeNode, bool, label) +IF_RET_ADD_END_STACK(6) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API bool TreeNode(const char* str_id, const char* fmt, ...) IM_FMTARGS(2); // read the FAQ about why and how to use ID. to align arbitrary text at the same level as a TreeNode() you can use Bullet(). +// Unsupported arg type ...) IM_FMTARGS(2 +// IMGUI_API bool TreeNode(const void* ptr_id, const char* fmt, ...) IM_FMTARGS(2); // " +// Unsupported arg type const void* ptr_id +// Unsupported arg type ...) IM_FMTARGS(2 +// IMGUI_API bool TreeNodeV(const char* str_id, const char* fmt, va_list args) IM_FMTLIST(2); +// Unsupported arg type va_list args) IM_FMTLIST(2 +// IMGUI_API bool TreeNodeV(const void* ptr_id, const char* fmt, va_list args) IM_FMTLIST(2); +// Unsupported arg type const void* ptr_id +// Unsupported arg type va_list args) IM_FMTLIST(2 +// IMGUI_API bool TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags = 0); +// Unsupported arg type ImGuiTreeNodeFlags flags = 0 +// IMGUI_API bool TreeNodeEx(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) IM_FMTARGS(3); +// Unsupported arg type ImGuiTreeNodeFlags flags +// Unsupported arg type ...) IM_FMTARGS(3 +// IMGUI_API bool TreeNodeEx(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) IM_FMTARGS(3); +// Unsupported arg type const void* ptr_id +// Unsupported arg type ImGuiTreeNodeFlags flags +// Unsupported arg type ...) IM_FMTARGS(3 +// IMGUI_API bool TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) IM_FMTLIST(3); +// Unsupported arg type ImGuiTreeNodeFlags flags +// Unsupported arg type va_list args) IM_FMTLIST(3 +// IMGUI_API bool TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) IM_FMTLIST(3); +// Unsupported arg type const void* ptr_id +// Unsupported arg type ImGuiTreeNodeFlags flags +// Unsupported arg type va_list args) IM_FMTLIST(3 +// IMGUI_API void TreePush(const char* str_id); // ~ Indent()+PushId(). Already called by TreeNode() when returning true, but you can call Push/Pop yourself for layout purpose +IMGUI_FUNCTION(TreePush) +LABEL_ARG(str_id) +CALL_FUNCTION_NO_RET(TreePush, str_id) +ADD_END_STACK(6) +END_IMGUI_FUNC +// IMGUI_API void TreePush(const void* ptr_id = NULL); // " +// Unsupported arg type const void* ptr_id = NULL +// IMGUI_API void TreePop(); // ~ Unindent()+PopId() +IMGUI_FUNCTION(TreePop) +CALL_FUNCTION_NO_RET(TreePop) +POP_END_STACK(6) +END_IMGUI_FUNC +// IMGUI_API void TreeAdvanceToLabelPos(); // advance cursor x position by GetTreeNodeToLabelSpacing() +IMGUI_FUNCTION(TreeAdvanceToLabelPos) +CALL_FUNCTION_NO_RET(TreeAdvanceToLabelPos) +END_IMGUI_FUNC +// IMGUI_API float GetTreeNodeToLabelSpacing(); // horizontal distance preceding label when using TreeNode*() or Bullet() == (g.FontSize + style.FramePadding.x*2) for a regular unframed TreeNode +IMGUI_FUNCTION(GetTreeNodeToLabelSpacing) +CALL_FUNCTION(GetTreeNodeToLabelSpacing, float) +PUSH_NUMBER(ret) +END_IMGUI_FUNC +// IMGUI_API void SetNextTreeNodeOpen(bool is_open, ImGuiCond cond = 0); // set next TreeNode/CollapsingHeader open state. +// Unsupported arg type ImGuiCond cond = 0 +// IMGUI_API bool CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags = 0); // if returning 'true' the header is open. doesn't indent nor push on ID stack. user doesn't have to call TreePop(). +// Unsupported arg type ImGuiTreeNodeFlags flags = 0 +// IMGUI_API bool CollapsingHeader(const char* label, bool* p_open, ImGuiTreeNodeFlags flags = 0); // when 'p_open' isn't NULL, display an additional small close button on upper right of the header +// Unsupported arg type ImGuiTreeNodeFlags flags = 0 +// IMGUI_API bool Selectable(const char* label, bool selected = false, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2 0 0); // "bool selected" carry the selection state (read-only). Selectable() is clicked is returns true so you can modify your selection state. size.x==0.0: use remaining width, size.x>0.0: specify width. size.y==0.0: use label height, size.y>0.0: specify height +IMGUI_FUNCTION(Selectable) +LABEL_ARG(label) +OPTIONAL_BOOL_ARG(selected, false) +OPTIONAL_INT_ARG(flags, 0) +OPTIONAL_IM_VEC_2_ARG(size, 0, 0) +CALL_FUNCTION(Selectable, bool, label, selected, flags, size) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API bool Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2 0 0); // "bool* p_selected" point to the selection state (read-write), as a convenient helper. +IMGUI_FUNCTION(Selectable_4) +LABEL_ARG(label) +BOOL_POINTER_ARG(p_selected) +OPTIONAL_INT_ARG(flags, 0) +OPTIONAL_IM_VEC_2_ARG(size, 0, 0) +CALL_FUNCTION(Selectable, bool, label, p_selected, flags, size) +PUSH_BOOL(ret) +END_BOOL_POINTER(p_selected) +END_IMGUI_FUNC +// IMGUI_API bool ListBox(const char* label, int* current_item, const char* const items[], int items_count, int height_in_items = -1); +// Unsupported arg type const char* const items[] +// IMGUI_API bool ListBox(const char* label, int* current_item, bool (*items_getter)(void* data, int idx, const char** out_text), void* data, int items_count, int height_in_items = -1); +// Unsupported arg type bool (*items_getter)(void* data +// Unsupported arg type const char** out_text) +// Unsupported arg type void* data +// IMGUI_API bool ListBoxHeader(const char* label, const ImVec2& size = ImVec2 0 0); // use if you want to reimplement ListBox() will custom data or interactions. make sure to call ListBoxFooter() afterwards. +IMGUI_FUNCTION(ListBoxHeader) +LABEL_ARG(label) +OPTIONAL_IM_VEC_2_ARG(size, 0, 0) +CALL_FUNCTION(ListBoxHeader, bool, label, size) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API bool ListBoxHeader(const char* label, int items_count, int height_in_items = -1); // " +IMGUI_FUNCTION(ListBoxHeader_3) +LABEL_ARG(label) +INT_ARG(items_count) +OPTIONAL_INT_ARG(height_in_items, -1) +CALL_FUNCTION(ListBoxHeader, bool, label, items_count, height_in_items) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API void ListBoxFooter(); // terminate the scrolling region +IMGUI_FUNCTION(ListBoxFooter) +CALL_FUNCTION_NO_RET(ListBoxFooter) +END_IMGUI_FUNC +// IMGUI_API void Value(const char* prefix, bool b); +IMGUI_FUNCTION(Value) +LABEL_ARG(prefix) +BOOL_ARG(b) +CALL_FUNCTION_NO_RET(Value, prefix, b) +END_IMGUI_FUNC +// IMGUI_API void Value(const char* prefix, int v); +IMGUI_FUNCTION(Value_2) +LABEL_ARG(prefix) +INT_ARG(v) +CALL_FUNCTION_NO_RET(Value, prefix, v) +END_IMGUI_FUNC +// IMGUI_API void Value(const char* prefix, unsigned int v); +IMGUI_FUNCTION(Value_2_2) +LABEL_ARG(prefix) +UINT_ARG(v) +CALL_FUNCTION_NO_RET(Value, prefix, v) +END_IMGUI_FUNC +// IMGUI_API void Value(const char* prefix, float v, const char* float_format = NULL); +IMGUI_FUNCTION(Value_3) +LABEL_ARG(prefix) +NUMBER_ARG(v) +OPTIONAL_LABEL_ARG(float_format) +CALL_FUNCTION_NO_RET(Value, prefix, v, float_format) +END_IMGUI_FUNC +// IMGUI_API void SetTooltip(const char* fmt, ...) IM_FMTARGS(1); // set text tooltip under mouse-cursor, typically use with ImGui::IsItemHovered(). overidde any previous call to SetTooltip(). +// Unsupported arg type ...) IM_FMTARGS(1 +// IMGUI_API void SetTooltipV(const char* fmt, va_list args) IM_FMTLIST(1); +// Unsupported arg type va_list args) IM_FMTLIST(1 +// IMGUI_API void BeginTooltip(); // begin/append a tooltip window. to create full-featured tooltip (with any kind of contents). +IMGUI_FUNCTION(BeginTooltip) +CALL_FUNCTION_NO_RET(BeginTooltip) +ADD_END_STACK(7) +END_IMGUI_FUNC +// IMGUI_API void EndTooltip(); +IMGUI_FUNCTION(EndTooltip) +CALL_FUNCTION_NO_RET(EndTooltip) +POP_END_STACK(7) +END_IMGUI_FUNC +// IMGUI_API bool BeginMainMenuBar(); // create and append to a full screen menu-bar. +IMGUI_FUNCTION(BeginMainMenuBar) +CALL_FUNCTION(BeginMainMenuBar, bool) +IF_RET_ADD_END_STACK(8) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API void EndMainMenuBar(); // only call EndMainMenuBar() if BeginMainMenuBar() returns true! +IMGUI_FUNCTION(EndMainMenuBar) +CALL_FUNCTION_NO_RET(EndMainMenuBar) +POP_END_STACK(8) +END_IMGUI_FUNC +// IMGUI_API bool BeginMenuBar(); // append to menu-bar of current window (requires ImGuiWindowFlags_MenuBar flag set on parent window). +IMGUI_FUNCTION(BeginMenuBar) +CALL_FUNCTION(BeginMenuBar, bool) +IF_RET_ADD_END_STACK(9) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API void EndMenuBar(); // only call EndMenuBar() if BeginMenuBar() returns true! +IMGUI_FUNCTION(EndMenuBar) +CALL_FUNCTION_NO_RET(EndMenuBar) +POP_END_STACK(9) +END_IMGUI_FUNC +// IMGUI_API bool BeginMenu(const char* label, bool enabled = true); // create a sub-menu entry. only call EndMenu() if this returns true! +IMGUI_FUNCTION(BeginMenu) +LABEL_ARG(label) +OPTIONAL_BOOL_ARG(enabled, true) +CALL_FUNCTION(BeginMenu, bool, label, enabled) +IF_RET_ADD_END_STACK(10) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API void EndMenu(); // only call EndMenu() if BeginMenu() returns true! +IMGUI_FUNCTION(EndMenu) +CALL_FUNCTION_NO_RET(EndMenu) +POP_END_STACK(10) +END_IMGUI_FUNC +// IMGUI_API bool MenuItem(const char* label, const char* shortcut = NULL, bool selected = false, bool enabled = true); // return true when activated. shortcuts are displayed for convenience but not processed by ImGui at the moment +IMGUI_FUNCTION(MenuItem) +LABEL_ARG(label) +OPTIONAL_LABEL_ARG(shortcut) +OPTIONAL_BOOL_ARG(selected, false) +OPTIONAL_BOOL_ARG(enabled, true) +CALL_FUNCTION(MenuItem, bool, label, shortcut, selected, enabled) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API bool MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled = true); // return true when activated + toggle (*p_selected) if p_selected != NULL +IMGUI_FUNCTION(MenuItem_4) +LABEL_ARG(label) +LABEL_ARG(shortcut) +BOOL_POINTER_ARG(p_selected) +OPTIONAL_BOOL_ARG(enabled, true) +CALL_FUNCTION(MenuItem, bool, label, shortcut, p_selected, enabled) +PUSH_BOOL(ret) +END_BOOL_POINTER(p_selected) +END_IMGUI_FUNC +// IMGUI_API void OpenPopup(const char* str_id); // call to mark popup as open (don't call every frame!). popups are closed when user click outside, or if CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block. By default, Selectable()/MenuItem() are calling CloseCurrentPopup(). Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level). +IMGUI_FUNCTION(OpenPopup) +LABEL_ARG(str_id) +CALL_FUNCTION_NO_RET(OpenPopup, str_id) +END_IMGUI_FUNC +// IMGUI_API bool BeginPopup(const char* str_id, ImGuiWindowFlags flags = 0); // return true if the popup is open, and you can start outputting to it. only call EndPopup() if BeginPopup() returns true! +IMGUI_FUNCTION(BeginPopup) +LABEL_ARG(str_id) +OPTIONAL_INT_ARG(flags, 0) +CALL_FUNCTION(BeginPopup, bool, str_id, flags) +IF_RET_ADD_END_STACK(11) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API bool BeginPopupContextItem(const char* str_id = NULL, int mouse_button = 1); // helper to open and begin popup when clicked on last item. if you can pass a NULL str_id only if the previous item had an id. If you want to use that on a non-interactive item such as Text() you need to pass in an explicit ID here. read comments in .cpp! +IMGUI_FUNCTION(BeginPopupContextItem) +OPTIONAL_LABEL_ARG(str_id) +OPTIONAL_INT_ARG(mouse_button, 1) +CALL_FUNCTION(BeginPopupContextItem, bool, str_id, mouse_button) +IF_RET_ADD_END_STACK(11) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API bool BeginPopupContextWindow(const char* str_id = NULL, int mouse_button = 1, bool also_over_items = true); // helper to open and begin popup when clicked on current window. +IMGUI_FUNCTION(BeginPopupContextWindow) +OPTIONAL_LABEL_ARG(str_id) +OPTIONAL_INT_ARG(mouse_button, 1) +OPTIONAL_BOOL_ARG(also_over_items, true) +CALL_FUNCTION(BeginPopupContextWindow, bool, str_id, mouse_button, also_over_items) +IF_RET_ADD_END_STACK(11) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API bool BeginPopupContextVoid(const char* str_id = NULL, int mouse_button = 1); // helper to open and begin popup when clicked in void (where there are no imgui windows). +IMGUI_FUNCTION(BeginPopupContextVoid) +OPTIONAL_LABEL_ARG(str_id) +OPTIONAL_INT_ARG(mouse_button, 1) +CALL_FUNCTION(BeginPopupContextVoid, bool, str_id, mouse_button) +IF_RET_ADD_END_STACK(11) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API bool BeginPopupModal(const char* name, bool* p_open = NULL, ImGuiWindowFlags flags = 0); // modal dialog (regular window with title bar, block interactions behind the modal window, can't close the modal window by clicking outside) +IMGUI_FUNCTION(BeginPopupModal) +LABEL_ARG(name) +OPTIONAL_BOOL_POINTER_ARG(p_open) +OPTIONAL_INT_ARG(flags, 0) +CALL_FUNCTION(BeginPopupModal, bool, name, p_open, flags) +IF_RET_ADD_END_STACK(11) +PUSH_BOOL(ret) +END_BOOL_POINTER(p_open) +END_IMGUI_FUNC +// IMGUI_API void EndPopup(); // only call EndPopup() if BeginPopupXXX() returns true! +IMGUI_FUNCTION(EndPopup) +CALL_FUNCTION_NO_RET(EndPopup) +POP_END_STACK(11) +END_IMGUI_FUNC +// IMGUI_API bool OpenPopupOnItemClick(const char* str_id = NULL, int mouse_button = 1); // helper to open popup when clicked on last item. return true when just opened. +IMGUI_FUNCTION(OpenPopupOnItemClick) +OPTIONAL_LABEL_ARG(str_id) +OPTIONAL_INT_ARG(mouse_button, 1) +CALL_FUNCTION(OpenPopupOnItemClick, bool, str_id, mouse_button) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API bool IsPopupOpen(const char* str_id); // return true if the popup is open +IMGUI_FUNCTION(IsPopupOpen) +LABEL_ARG(str_id) +CALL_FUNCTION(IsPopupOpen, bool, str_id) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API void CloseCurrentPopup(); // close the popup we have begin-ed into. clicking on a MenuItem or Selectable automatically close the current popup. +IMGUI_FUNCTION(CloseCurrentPopup) +CALL_FUNCTION_NO_RET(CloseCurrentPopup) +END_IMGUI_FUNC +// IMGUI_API void Columns(int count = 1, const char* id = NULL, bool border = true); +IMGUI_FUNCTION(Columns) +OPTIONAL_INT_ARG(count, 1) +OPTIONAL_LABEL_ARG(id) +OPTIONAL_BOOL_ARG(border, true) +CALL_FUNCTION_NO_RET(Columns, count, id, border) +END_IMGUI_FUNC +// IMGUI_API void NextColumn(); // next column, defaults to current row or next row if the current row is finished +IMGUI_FUNCTION(NextColumn) +CALL_FUNCTION_NO_RET(NextColumn) +END_IMGUI_FUNC +// IMGUI_API int GetColumnIndex(); // get current column index +// Unsupported return type int +// IMGUI_API float GetColumnWidth(int column_index = -1); // get column width (in pixels). pass -1 to use current column +IMGUI_FUNCTION(GetColumnWidth) +OPTIONAL_INT_ARG(column_index, -1) +CALL_FUNCTION(GetColumnWidth, float, column_index) +PUSH_NUMBER(ret) +END_IMGUI_FUNC +// IMGUI_API void SetColumnWidth(int column_index, float width); // set column width (in pixels). pass -1 to use current column +IMGUI_FUNCTION(SetColumnWidth) +INT_ARG(column_index) +NUMBER_ARG(width) +CALL_FUNCTION_NO_RET(SetColumnWidth, column_index, width) +END_IMGUI_FUNC +// IMGUI_API float GetColumnOffset(int column_index = -1); // get position of column line (in pixels, from the left side of the contents region). pass -1 to use current column, otherwise 0..GetColumnsCount() inclusive. column 0 is typically 0.0f +IMGUI_FUNCTION(GetColumnOffset) +OPTIONAL_INT_ARG(column_index, -1) +CALL_FUNCTION(GetColumnOffset, float, column_index) +PUSH_NUMBER(ret) +END_IMGUI_FUNC +// IMGUI_API void SetColumnOffset(int column_index, float offset_x); // set position of column line (in pixels, from the left side of the contents region). pass -1 to use current column +IMGUI_FUNCTION(SetColumnOffset) +INT_ARG(column_index) +NUMBER_ARG(offset_x) +CALL_FUNCTION_NO_RET(SetColumnOffset, column_index, offset_x) +END_IMGUI_FUNC +// IMGUI_API int GetColumnsCount(); +// Unsupported return type int +// IMGUI_API void LogToTTY(int max_depth = -1); // start logging to tty +IMGUI_FUNCTION(LogToTTY) +OPTIONAL_INT_ARG(max_depth, -1) +CALL_FUNCTION_NO_RET(LogToTTY, max_depth) +END_IMGUI_FUNC +// IMGUI_API void LogToFile(int max_depth = -1, const char* filename = NULL); // start logging to file +IMGUI_FUNCTION(LogToFile) +OPTIONAL_INT_ARG(max_depth, -1) +OPTIONAL_LABEL_ARG(filename) +CALL_FUNCTION_NO_RET(LogToFile, max_depth, filename) +END_IMGUI_FUNC +// IMGUI_API void LogToClipboard(int max_depth = -1); // start logging to OS clipboard +IMGUI_FUNCTION(LogToClipboard) +OPTIONAL_INT_ARG(max_depth, -1) +CALL_FUNCTION_NO_RET(LogToClipboard, max_depth) +END_IMGUI_FUNC +// IMGUI_API void LogFinish(); // stop logging (close file, etc.) +IMGUI_FUNCTION(LogFinish) +CALL_FUNCTION_NO_RET(LogFinish) +END_IMGUI_FUNC +// IMGUI_API void LogButtons(); // helper to display buttons for logging to tty/file/clipboard +IMGUI_FUNCTION(LogButtons) +CALL_FUNCTION_NO_RET(LogButtons) +END_IMGUI_FUNC +// IMGUI_API void LogText(const char* fmt, ...) IM_FMTARGS(1); // pass text data straight to log (without being displayed) +// Unsupported arg type ...) IM_FMTARGS(1 +// IMGUI_API bool BeginDragDropSource(ImGuiDragDropFlags flags = 0); // call when the current item is active. If this return true, you can call SetDragDropPayload() + EndDragDropSource() +// Unsupported arg type ImGuiDragDropFlags flags = 0 +// IMGUI_API bool SetDragDropPayload(const char* type, const void* data, size_t size, ImGuiCond cond = 0);// type is a user defined string of maximum 32 characters. Strings starting with '_' are reserved for dear imgui internal types. Data is copied and held by imgui. +// Unsupported arg type const void* data +// Unsupported arg type size_t size +// Unsupported arg type ImGuiCond cond = 0 +// IMGUI_API void EndDragDropSource(); // only call EndDragDropSource() if BeginDragDropSource() returns true! +IMGUI_FUNCTION(EndDragDropSource) +CALL_FUNCTION_NO_RET(EndDragDropSource) +POP_END_STACK(12) +END_IMGUI_FUNC +// IMGUI_API bool BeginDragDropTarget(); // call after submitting an item that may receive an item. If this returns true, you can call AcceptDragDropPayload() + EndDragDropTarget() +IMGUI_FUNCTION(BeginDragDropTarget) +CALL_FUNCTION(BeginDragDropTarget, bool) +IF_RET_ADD_END_STACK(13) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API const ImGuiPayload* AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags = 0); // accept contents of a given type. If ImGuiDragDropFlags_AcceptBeforeDelivery is set you can peek into the payload before the mouse button is released. +// Unsupported return type const +// Unsupported arg type ImGuiDragDropFlags flags = 0 +// IMGUI_API void EndDragDropTarget(); // only call EndDragDropTarget() if BeginDragDropTarget() returns true! +IMGUI_FUNCTION(EndDragDropTarget) +CALL_FUNCTION_NO_RET(EndDragDropTarget) +POP_END_STACK(13) +END_IMGUI_FUNC +// IMGUI_API void PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect); +IMGUI_FUNCTION(PushClipRect) +IM_VEC_2_ARG(clip_rect_min) +IM_VEC_2_ARG(clip_rect_max) +BOOL_ARG(intersect_with_current_clip_rect) +CALL_FUNCTION_NO_RET(PushClipRect, clip_rect_min, clip_rect_max, intersect_with_current_clip_rect) +END_IMGUI_FUNC +// IMGUI_API void PopClipRect(); +IMGUI_FUNCTION(PopClipRect) +CALL_FUNCTION_NO_RET(PopClipRect) +END_IMGUI_FUNC +// IMGUI_API void SetItemDefaultFocus(); // make last item the default focused item of a window. Please use instead of "if (IsWindowAppearing()) SetScrollHere()" to signify "default item". +IMGUI_FUNCTION(SetItemDefaultFocus) +CALL_FUNCTION_NO_RET(SetItemDefaultFocus) +END_IMGUI_FUNC +// IMGUI_API void SetKeyboardFocusHere(int offset = 0); // focus keyboard on the next widget. Use positive 'offset' to access sub components of a multiple component widget. Use -1 to access previous widget. +IMGUI_FUNCTION(SetKeyboardFocusHere) +OPTIONAL_INT_ARG(offset, 0) +CALL_FUNCTION_NO_RET(SetKeyboardFocusHere, offset) +END_IMGUI_FUNC +// IMGUI_API bool IsItemHovered(ImGuiHoveredFlags flags = 0); // is the last item hovered? (and usable, aka not blocked by a popup, etc.). See ImGuiHoveredFlags for more options. +// Unsupported arg type ImGuiHoveredFlags flags = 0 +// IMGUI_API bool IsItemActive(); // is the last item active? (e.g. button being held, text field being edited- items that don't interact will always return false) +IMGUI_FUNCTION(IsItemActive) +CALL_FUNCTION(IsItemActive, bool) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API bool IsItemFocused(); // is the last item focused for keyboard/gamepad navigation? +IMGUI_FUNCTION(IsItemFocused) +CALL_FUNCTION(IsItemFocused, bool) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API bool IsItemClicked(int mouse_button = 0); // is the last item clicked? (e.g. button/node just clicked on) +IMGUI_FUNCTION(IsItemClicked) +OPTIONAL_INT_ARG(mouse_button, 0) +CALL_FUNCTION(IsItemClicked, bool, mouse_button) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API bool IsItemVisible(); // is the last item visible? (aka not out of sight due to clipping/scrolling.) +IMGUI_FUNCTION(IsItemVisible) +CALL_FUNCTION(IsItemVisible, bool) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API bool IsAnyItemHovered(); +IMGUI_FUNCTION(IsAnyItemHovered) +CALL_FUNCTION(IsAnyItemHovered, bool) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API bool IsAnyItemActive(); +IMGUI_FUNCTION(IsAnyItemActive) +CALL_FUNCTION(IsAnyItemActive, bool) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API bool IsAnyItemFocused(); +IMGUI_FUNCTION(IsAnyItemFocused) +CALL_FUNCTION(IsAnyItemFocused, bool) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API ImVec2 GetItemRectMin(); // get bounding rectangle of last item, in screen space +IMGUI_FUNCTION(GetItemRectMin) +CALL_FUNCTION(GetItemRectMin, ImVec2) +PUSH_NUMBER(ret.x) +PUSH_NUMBER(ret.y) +END_IMGUI_FUNC +// IMGUI_API ImVec2 GetItemRectMax(); // " +IMGUI_FUNCTION(GetItemRectMax) +CALL_FUNCTION(GetItemRectMax, ImVec2) +PUSH_NUMBER(ret.x) +PUSH_NUMBER(ret.y) +END_IMGUI_FUNC +// IMGUI_API ImVec2 GetItemRectSize(); // get size of last item, in screen space +IMGUI_FUNCTION(GetItemRectSize) +CALL_FUNCTION(GetItemRectSize, ImVec2) +PUSH_NUMBER(ret.x) +PUSH_NUMBER(ret.y) +END_IMGUI_FUNC +// IMGUI_API void SetItemAllowOverlap(); // allow last item to be overlapped by a subsequent item. sometimes useful with invisible buttons, selectables, etc. to catch unused area. +IMGUI_FUNCTION(SetItemAllowOverlap) +CALL_FUNCTION_NO_RET(SetItemAllowOverlap) +END_IMGUI_FUNC +// IMGUI_API bool IsRectVisible(const ImVec2& size); // test if rectangle (of given size, starting from cursor position) is visible / not clipped. +IMGUI_FUNCTION(IsRectVisible) +IM_VEC_2_ARG(size) +CALL_FUNCTION(IsRectVisible, bool, size) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API bool IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max); // test if rectangle (in screen space) is visible / not clipped. to perform coarse clipping on user's side. +IMGUI_FUNCTION(IsRectVisible_2) +IM_VEC_2_ARG(rect_min) +IM_VEC_2_ARG(rect_max) +CALL_FUNCTION(IsRectVisible, bool, rect_min, rect_max) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API float GetTime(); +IMGUI_FUNCTION(GetTime) +CALL_FUNCTION(GetTime, float) +PUSH_NUMBER(ret) +END_IMGUI_FUNC +// IMGUI_API int GetFrameCount(); +// Unsupported return type int +// IMGUI_API ImDrawList* GetOverlayDrawList(); // this draw list will be the last rendered one, useful to quickly draw overlays shapes/text +// Unsupported return type ImDrawList* +// IMGUI_API ImDrawListSharedData* GetDrawListSharedData(); // you may use this when creating your own ImDrawList instances +// Unsupported return type ImDrawListSharedData* +// IMGUI_API const char* GetStyleColorName(ImGuiCol idx); +// Unsupported return type const +// IMGUI_API void SetStateStorage(ImGuiStorage* storage); // replace current window storage with our own (if you want to manipulate it yourself, typically clear subsection of it) +// Unsupported arg type ImGuiStorage* storage +// IMGUI_API ImGuiStorage* GetStateStorage(); +// Unsupported return type ImGuiStorage* +// IMGUI_API ImVec2 CalcTextSize(const char* text, const char* text_end = NULL, bool hide_text_after_double_hash = false, float wrap_width = -1.0f); +IMGUI_FUNCTION(CalcTextSize) +LABEL_ARG(text) +OPTIONAL_LABEL_ARG(text_end) +OPTIONAL_BOOL_ARG(hide_text_after_double_hash, false) +OPTIONAL_NUMBER_ARG(wrap_width, -1.0f) +CALL_FUNCTION(CalcTextSize, ImVec2, text, text_end, hide_text_after_double_hash, wrap_width) +PUSH_NUMBER(ret.x) +PUSH_NUMBER(ret.y) +END_IMGUI_FUNC +// IMGUI_API void CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end); // calculate coarse clipping for large list of evenly sized items. Prefer using the ImGuiListClipper higher-level helper if you can. +IMGUI_FUNCTION(CalcListClipping) +INT_ARG(items_count) +NUMBER_ARG(items_height) +INT_POINTER_ARG(out_items_display_start) +INT_POINTER_ARG(out_items_display_end) +CALL_FUNCTION_NO_RET(CalcListClipping, items_count, items_height, out_items_display_start, out_items_display_end) +END_INT_POINTER(out_items_display_start) +END_INT_POINTER(out_items_display_end) +END_IMGUI_FUNC +// IMGUI_API bool BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags flags = 0); // helper to create a child window / scrolling region that looks like a normal widget frame +IMGUI_FUNCTION(BeginChildFrame) +UINT_ARG(id) +IM_VEC_2_ARG(size) +OPTIONAL_INT_ARG(flags, 0) +CALL_FUNCTION(BeginChildFrame, bool, id, size, flags) +IF_RET_ADD_END_STACK(14) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API void EndChildFrame(); // always call EndChildFrame() regardless of BeginChildFrame() return values (which indicates a collapsed/clipped window) +IMGUI_FUNCTION(EndChildFrame) +CALL_FUNCTION_NO_RET(EndChildFrame) +POP_END_STACK(14) +END_IMGUI_FUNC +// IMGUI_API ImVec4 ColorConvertU32ToFloat4(ImU32 in); +// Unsupported return type ImVec4 +// IMGUI_API ImU32 ColorConvertFloat4ToU32(const ImVec4& in); +IMGUI_FUNCTION(ColorConvertFloat4ToU32) +IM_VEC_4_ARG(in) +CALL_FUNCTION(ColorConvertFloat4ToU32, unsigned int, in) +PUSH_NUMBER(ret) +END_IMGUI_FUNC +// IMGUI_API void ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v); +// Unsupported arg type float& out_h +// Unsupported arg type float& out_s +// Unsupported arg type float& out_v +// IMGUI_API void ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b); +// Unsupported arg type float& out_r +// Unsupported arg type float& out_g +// Unsupported arg type float& out_b +// IMGUI_API int GetKeyIndex(ImGuiKey imgui_key); // map ImGuiKey_* values into user's key index. == io.KeyMap[key] +// Unsupported return type int +// IMGUI_API bool IsKeyDown(int user_key_index); // is key being held. == io.KeysDown[user_key_index]. note that imgui doesn't know the semantic of each entry of io.KeyDown[]. Use your own indices/enums according to how your backend/engine stored them into KeyDown[]! +IMGUI_FUNCTION(IsKeyDown) +INT_ARG(user_key_index) +CALL_FUNCTION(IsKeyDown, bool, user_key_index) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API bool IsKeyPressed(int user_key_index, bool repeat = true); // was key pressed (went from !Down to Down). if repeat=true, uses io.KeyRepeatDelay / KeyRepeatRate +IMGUI_FUNCTION(IsKeyPressed) +INT_ARG(user_key_index) +OPTIONAL_BOOL_ARG(repeat, true) +CALL_FUNCTION(IsKeyPressed, bool, user_key_index, repeat) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API bool IsKeyReleased(int user_key_index); // was key released (went from Down to !Down).. +IMGUI_FUNCTION(IsKeyReleased) +INT_ARG(user_key_index) +CALL_FUNCTION(IsKeyReleased, bool, user_key_index) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API int GetKeyPressedAmount(int key_index, float repeat_delay, float rate); // uses provided repeat rate/delay. return a count, most often 0 or 1 but might be >1 if RepeatRate is small enough that DeltaTime > RepeatRate +// Unsupported return type int +// IMGUI_API bool IsMouseDown(int button); // is mouse button held +IMGUI_FUNCTION(IsMouseDown) +INT_ARG(button) +CALL_FUNCTION(IsMouseDown, bool, button) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API bool IsAnyMouseDown(); // is any mouse button held +IMGUI_FUNCTION(IsAnyMouseDown) +CALL_FUNCTION(IsAnyMouseDown, bool) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API bool IsMouseClicked(int button, bool repeat = false); // did mouse button clicked (went from !Down to Down) +IMGUI_FUNCTION(IsMouseClicked) +INT_ARG(button) +OPTIONAL_BOOL_ARG(repeat, false) +CALL_FUNCTION(IsMouseClicked, bool, button, repeat) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API bool IsMouseDoubleClicked(int button); // did mouse button double-clicked. a double-click returns false in IsMouseClicked(). uses io.MouseDoubleClickTime. +IMGUI_FUNCTION(IsMouseDoubleClicked) +INT_ARG(button) +CALL_FUNCTION(IsMouseDoubleClicked, bool, button) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API bool IsMouseReleased(int button); // did mouse button released (went from Down to !Down) +IMGUI_FUNCTION(IsMouseReleased) +INT_ARG(button) +CALL_FUNCTION(IsMouseReleased, bool, button) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API bool IsMouseDragging(int button = 0, float lock_threshold = -1.0f); // is mouse dragging. if lock_threshold < -1.0f uses io.MouseDraggingThreshold +IMGUI_FUNCTION(IsMouseDragging) +OPTIONAL_INT_ARG(button, 0) +OPTIONAL_NUMBER_ARG(lock_threshold, -1.0f) +CALL_FUNCTION(IsMouseDragging, bool, button, lock_threshold) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API bool IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip = true); // is mouse hovering given bounding rect (in screen space). clipped by current clipping settings. disregarding of consideration of focus/window ordering/blocked by a popup. +IMGUI_FUNCTION(IsMouseHoveringRect) +IM_VEC_2_ARG(r_min) +IM_VEC_2_ARG(r_max) +OPTIONAL_BOOL_ARG(clip, true) +CALL_FUNCTION(IsMouseHoveringRect, bool, r_min, r_max, clip) +PUSH_BOOL(ret) +END_IMGUI_FUNC +// IMGUI_API bool IsMousePosValid(const ImVec2* mouse_pos = NULL); // +// Unsupported arg type const ImVec2* mouse_pos = NULL +// IMGUI_API ImVec2 GetMousePos(); // shortcut to ImGui::GetIO().MousePos provided by user, to be consistent with other calls +IMGUI_FUNCTION(GetMousePos) +CALL_FUNCTION(GetMousePos, ImVec2) +PUSH_NUMBER(ret.x) +PUSH_NUMBER(ret.y) +END_IMGUI_FUNC +// IMGUI_API ImVec2 GetMousePosOnOpeningCurrentPopup(); // retrieve backup of mouse position at the time of opening popup we have BeginPopup() into +IMGUI_FUNCTION(GetMousePosOnOpeningCurrentPopup) +CALL_FUNCTION(GetMousePosOnOpeningCurrentPopup, ImVec2) +PUSH_NUMBER(ret.x) +PUSH_NUMBER(ret.y) +END_IMGUI_FUNC +// IMGUI_API ImVec2 GetMouseDragDelta(int button = 0, float lock_threshold = -1.0f); // dragging amount since clicking. if lock_threshold < -1.0f uses io.MouseDraggingThreshold +IMGUI_FUNCTION(GetMouseDragDelta) +OPTIONAL_INT_ARG(button, 0) +OPTIONAL_NUMBER_ARG(lock_threshold, -1.0f) +CALL_FUNCTION(GetMouseDragDelta, ImVec2, button, lock_threshold) +PUSH_NUMBER(ret.x) +PUSH_NUMBER(ret.y) +END_IMGUI_FUNC +// IMGUI_API void ResetMouseDragDelta(int button = 0); // +IMGUI_FUNCTION(ResetMouseDragDelta) +OPTIONAL_INT_ARG(button, 0) +CALL_FUNCTION_NO_RET(ResetMouseDragDelta, button) +END_IMGUI_FUNC +// IMGUI_API ImGuiMouseCursor GetMouseCursor(); // get desired cursor type, reset in ImGui::NewFrame(), this is updated during the frame. valid before Render(). If you use software rendering by setting io.MouseDrawCursor ImGui will render those for you +// Unsupported return type ImGuiMouseCursor +// IMGUI_API void SetMouseCursor(ImGuiMouseCursor type); // set desired cursor type +IMGUI_FUNCTION(SetMouseCursor) +INT_ARG(type) +CALL_FUNCTION_NO_RET(SetMouseCursor, type) +END_IMGUI_FUNC +// IMGUI_API void CaptureKeyboardFromApp(bool capture = true); // manually override io.WantCaptureKeyboard flag next frame (said flag is entirely left for your application to handle). e.g. force capture keyboard when your widget is being hovered. +IMGUI_FUNCTION(CaptureKeyboardFromApp) +OPTIONAL_BOOL_ARG(capture, true) +CALL_FUNCTION_NO_RET(CaptureKeyboardFromApp, capture) +END_IMGUI_FUNC +// IMGUI_API void CaptureMouseFromApp(bool capture = true); // manually override io.WantCaptureMouse flag next frame (said flag is entirely left for your application to handle). +IMGUI_FUNCTION(CaptureMouseFromApp) +OPTIONAL_BOOL_ARG(capture, true) +CALL_FUNCTION_NO_RET(CaptureMouseFromApp, capture) +END_IMGUI_FUNC +// IMGUI_API const char* GetClipboardText(); +// Unsupported return type const +// IMGUI_API void SetClipboardText(const char* text); +IMGUI_FUNCTION(SetClipboardText) +LABEL_ARG(text) +CALL_FUNCTION_NO_RET(SetClipboardText, text) +END_IMGUI_FUNC +// IMGUI_API void SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void(*free_func)(void* ptr, void* user_data), void* user_data = NULL); +// Unsupported arg type void* (*alloc_func)(size_t sz +// Unsupported arg type void* user_data) +// Unsupported arg type void(*free_func)(void* ptr +// Unsupported arg type void* user_data) +// Unsupported arg type void* user_data = NULL +// IMGUI_API void* MemAlloc(size_t size); +// Unsupported return type void* +// Unsupported arg type size_t size +// IMGUI_API void MemFree(void* ptr); +// Unsupported arg type void* ptr +END_STACK_START +END_STACK_OPTION(0, EndFrame) +END_STACK_OPTION(1, End) +END_STACK_OPTION(2, EndChild) +END_STACK_OPTION(3, PopStyleVar) +END_STACK_OPTION(4, EndGroup) +END_STACK_OPTION(5, EndCombo) +END_STACK_OPTION(6, TreePop) +END_STACK_OPTION(7, EndTooltip) +END_STACK_OPTION(8, EndMainMenuBar) +END_STACK_OPTION(9, EndMenuBar) +END_STACK_OPTION(10, EndMenu) +END_STACK_OPTION(11, EndPopup) +END_STACK_OPTION(12, EndDragDropSource) +END_STACK_OPTION(13, EndDragDropTarget) +END_STACK_OPTION(14, EndChildFrame) +END_STACK_END +//enum ImGuiWindowFlags_ + +//enum ImGuiInputTextFlags_ + +//enum ImGuiTreeNodeFlags_ + +//enum ImGuiSelectableFlags_ + +//enum ImGuiComboFlags_ + +//enum ImGuiFocusedFlags_ + +//enum ImGuiHoveredFlags_ + +//enum ImGuiDragDropFlags_ + +//enum ImGuiDir_ + +//enum ImGuiKey_ + +//enum ImGuiNavInput_ + +//enum ImGuiConfigFlags_ + +//enum ImGuiBackendFlags_ + +//enum ImGuiCol_ + +//enum ImGuiStyleVar_ + +//enum ImGuiColorEditFlags_ + +//enum ImGuiMouseCursor_ + +//enum ImGuiCond_ + +//struct ImGuiStyle + +//struct ImGuiIO + +//namespace ImGui + +END_STACK_START +END_STACK_END +//class ImVector + +//struct ImGuiOnceUponAFrame + +//struct ImGuiTextFilter + +//struct ImGuiTextBuffer + +//struct ImGuiStorage + +//struct ImGuiTextEditCallbackData + +//struct ImGuiSizeCallbackData + +//struct ImGuiPayload + +//struct ImColor + +//struct ImGuiListClipper + +//struct ImDrawCmd + +//struct ImDrawVert + +//struct ImDrawChannel + +//enum ImDrawCornerFlags_ + +//enum ImDrawListFlags_ + +//struct ImDrawList + +// IMGUI_API void DeIndexAllBuffers(); // Helper to convert all buffers from indexed to non-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering! + +/* +IMGUI_FUNCTION_DRAW_LIST(DeIndexAllBuffers) +DRAW_LIST_CALL_FUNCTION_NO_RET(DeIndexAllBuffers) +END_IMGUI_FUNC + +// IMGUI_API void ScaleClipRects(const ImVec2& sc); // Helper to scale the ClipRect field of each ImDrawCmd. Use if your final output buffer is at a different scale than ImGui expects, or if there is a difference between your window resolution and framebuffer resolution. +IMGUI_FUNCTION_DRAW_LIST(ScaleClipRects) +IM_VEC_2_ARG(sc) +DRAW_LIST_CALL_FUNCTION_NO_RET(ScaleClipRects, sc) +END_IMGUI_FUNC +*/ + +//struct ImDrawData + +//struct ImFontConfig + +//struct ImFontGlyph + +//enum ImFontAtlasFlags_ + +//struct ImFontAtlas +